Ongoing Data

You can request the latest data in three distinct ways via the API, depending on your needs for timing and granularity:

TypeFrequencyAvailability (UTC)Timezone Specific Times
Last SettlementDailyAfter midnight UTC- London 16:30
Last AssessmentsDaily (3 datapoints)After midnight UTC- Singapore 16:30 - London 16:30 - New York 14:30
IntradayEvery 15 minsReal-time- From 2:00 UTC to 20:00 UTC

Last Settlement

The last settlement reflects the final published value for the day. It is captured daily at the following local time:

  • 16:30 London (or 12:30 on early close days)

The data becomes available after midnight UTC.

Last Settlement for tenor

The last settlement data can be queried for a given tenor:

📘

Example

GET /ongoing/last-settlement/0007f4dd-673a-5309-b606-178aadbd2d2c/11-15 Jun 25

{
    "generatedOn": "2025-05-16T15:16:05.173",
    "price": 3.5788883413888546,
    "units": "$/mt",
    "tenor": "11-15 Jun 25",
    "dependencies": [
        {
            "id": "dd758b98-0227-5e68-8529-b35307373d27",
            "name": "RAS LAFFAN - BARCELONA VIACAPE Jet-A1 LR2 - Dlvd Price by Load",
            "value": -0.5788883413888545,
            "units": "$/mt",
            "tenorName": "11-15 Jun 25"
        },
        {
            "id": "cdc954af-529e-5f98-8a2e-c3543a2cbe81",
            "name": "RAS LAFFAN - BARCELONA VIACAPE Jet-A1 LR2 - Sales Price",
            "value": 3.0,
            "units": "$/mt",
            "tenorName": "21-25 Jul 25"
        }
    ],
    "metadata": [
        { "name": "loadDate", "value": "2025-06-15T00:00:00.000Z" },
        { "name": "deliveryDate", "value": "2025-07-25T00:00:00.000Z" }
        // ...
    ]
}

Last Settlement for all tenors

The last settlement data can also be queried for all available tenors:

📘

Example

GET /ongoing/last-settlement/03d82381-a711-5077-b8ba-f60b2047ae11

{
 "items": [
   {
     "generatedOn": "2025-08-15T15:15:00.000458",
     "price": 1.8,
     "units": "$/bbl",
     "tenor": "1-5 Dec 25",
     "dependencies": [
       {
         "id": "c4b52b35-1bc1-5f12-9fc5-40bc9727bd09",
         "name": "Indonesia 92 RON Grade - SING",
         "value": 1.7771965966071783,
         "units": "$/bbl",
         "tenorName": "1-5 Dec 25"
       },
       {
         "id": "bbad8d31-e1c0-5ac3-88e5-40ffcd17bc42",
         "name": "SING - Indonesia 92 RON - SING FOB - Freight Costs - By Load",
         "value": 0.03592,
         "units": "$/bbl",
         "tenorName": "1-5 Dec 25"
       }
     ],
     "metadata": [
       {
         "name": "monthName",
         "value": "Dec 25"
       },
       {
         "name": "monthNumber",
         "value": "12"
       },
       // ... additional metadata
     ]
   },
   // ... additional items
 ]
}

Last Assessments

The last assessments are captured at a consistent daily time across three regional markets:

  • 16:30 Singapore (12:30 early close)
  • 16:30 London (12:30 early close)
  • 14:30 New York (13:30 early close)

The latest assessment data is available after midnight UTC.

Last Assessments for tenor

The last assessments data can be queried for a given tenors:

📘

Example

GET /ongoing/last-assessments/0007f4dd-673a-5309-b606-178aadbd2d2c/11-15 Jun 25

{
    "singaporeSnapshot": {
        "generatedOn": "2025-05-16T08:16:04.515",
        "price": 5.615760777686308,
        "units": "$/mt",
        "tenor": "11-15 Jun 25",
        "dependencies": [
            {
                "id": "dd758b98-0227-5e68-8529-b35307373d27",
                "name": "RAS LAFFAN - BARCELONA VIACAPE Jet-A1 LR2 - Dlvd Price by Load",
                "value": -2.615760777686308,
                "units": "$/mt",
                "tenorName": "11-15 Jun 25"
            },
            {
                "id": "cdc954af-529e-5f98-8a2e-c3543a2cbe81",
                "name": "RAS LAFFAN - BARCELONA VIACAPE Jet-A1 LR2 - Sales Price",
                "value": 3.0,
                "units": "$/mt",
                "tenorName": "21-25 Jul 25"
            }
        ],
        "metadata": [
            { "name": "loadDate", "value": "2025-06-15T00:00:00.000Z" },
            { "name": "deliveryDate", "value": "2025-07-25T00:00:00.000Z" }
            // ...
        ]
    },
    "londonSnapshot": {
        // ...
    },
    "newYorkSnapshot": {
        // ...
    }
}

Last Assessments for all tenors

The last assessments data can also be queried for all available tenors:

📘

Example

GET /ongoing/last-assessments/03d82381-a711-5077-b8ba-f60b2047ae11

{
  "items": [
    {
      "tenor": "1-5 Dec 25",
      "singaporeSnapshot": {
        "generatedOn": "2025-08-15T08:15:00.001222",
        "price": 1.8,
        "units": "$/bbl",
        "tenor": "1-5 Dec 25",
        "dependencies": [
          {
            "id": "c4b52b35-1bc1-5f12-9fc5-40bc9727bd09",
            "name": "Indonesia 92 RON Grade - SING",
            "value": 1.7609948473138717,
            "units": "$/bbl",
            "tenorName": "1-5 Dec 25"
          },
          {
            "id": "bbad8d31-e1c0-5ac3-88e5-40ffcd17bc42",
            "name": "SING - Indonesia 92 RON - SING FOB - Freight Costs - By Load",
            "value": 0.03597,
            "units": "$/bbl",
            "tenorName": "1-5 Dec 25"
          }
        ],
        "metadata": [
          {
            "name": "monthName",
            "value": "Dec 25"
          },
          {
            "name": "monthNumber",
            "value": "12"
          },
          // ... additional metadata
        ]
      },
      "londonSnapshot": {
        // ...
      },
      "newYorkSnapshot": {
        // ...
      }
    },
    // ... additional items
  ]
}
> ```

Intraday

The intraday data provides the most granular view, offering historical and real-time updates.

  • Physical data updates every 15 minutes.
  • Dependencies (e.g. arbitrage routes, blends, premiums, cash differentials) also update within the same interval.
📘

Example

GET /ongoing/intraday/0007f4dd-673a-5309-b606-178aadbd2d2c/11-15 Jun 25

{
    "items": [
        {
            "generatedOn": "2025-05-19T11:46:04.619",
            "price": 5.587244205568989,
            "units": "$/mt",
            "tenor": "11-15 Jun 25",
            "dependencies": [
                {
                    "id": "dd758b98-0227-5e68-8529-b35307373d27",
                    "name": "RAS LAFFAN - BARCELONA VIACAPE Jet-A1 LR2 - Dlvd Price by Load",
                    "value": -2.587244205568988,
                    "units": "$/mt",
                    "tenorName": "11-15 Jun 25"
                },
                {
                    "id": "cdc954af-529e-5f98-8a2e-c3543a2cbe81",
                    "name": "RAS LAFFAN - BARCELONA VIACAPE Jet-A1 LR2 - Sales Price",
                    "value": 3.0,
                    "units": "$/mt",
                    "tenorName": "21-25 Jul 25"
                }
            ],
            "metadata": [
                { "name": "loadDate", "value": "2025-06-15T00:00:00.000Z" },
                { "name": "deliveryDate", "value": "2025-07-25T00:00:00.000Z" }
                // ...
            ]
        }
        // Additional intraday items...
    ],
    "self": "https://api.sparta.app/v2/ongoing/intraday/0007f4dd-673a-5309-b606-178aadbd2d2c/11-15%20Jun%2025?&page=1&pageSize=100",
    "next": null,
    "prev": null,
    "last": "https://api.sparta.app/v2/ongoing/intraday/0007f4dd-673a-5309-b606-178aadbd2d2c/11-15%20Jun%2025?&page=1&pageSize=100"
}

Bulk download last settlement and last assessments data

The last settlement and last assessments data can be downloaded in bulk for all tenors by leveraging the catalogue API.

For example, the python script below first retrieves the catalogue, then it asynchronously fetches the last assessments data.

NOTE: this is a code example only. Sparta does not provide guarantees or support this script.

import asyncio
import aiohttp
import time
import json
import requests
from urllib.parse import quote

def fetch_catalog(base_url, token):
    """Fetch complete catalog"""
    catalog = []
    url = f"{base_url}/v2/catalogue"
    page_count = 0
    
    print("📋 Fetching catalog...", end="")
    
    while url:
        page_count += 1
        print(".", end="")
        
        response = requests.get(url, headers={"Authorization": f"Bearer {token}"})
        
        if response.status_code == 200:
            data = response.json()
            if "items" in data:
                catalog.extend(data["items"])
            url = data.get("next")
        else:
            print(f"\n❌ Failed to fetch catalog page {page_count}: {response.status_code}")
            break
    
    return catalog, page_count

async def fetch_assessment_data(session, base_url, token, item_id, item_name, semaphore):
    """Fetch last-assessment data for a single catalog item (async)"""
    async with semaphore:  # Limit concurrent requests
        url = f"{base_url}/v2/ongoing/last-assessments/{item_id}"
        
        try:
            async with session.get(url, headers={"Authorization": f"Bearer {token}"}) as response:
                if response.status == 200:
                    data = await response.json()
                    return {
                        'item_id': item_id,
                        'item_name': item_name,
                        'data': data,
                        'status': 'success'
                    }
                else:
                    return {
                        'item_id': item_id,
                        'item_name': item_name,
                        'data': None,
                        'status': f'error_{response.status}'
                    }
        except Exception as e:
            return {
                'item_id': item_id,
                'item_name': item_name,
                'data': None,
                'status': f'exception_{str(e)[:50]}'
            }

async def fetch_assessments_async(catalog_items, base_url, token, max_concurrent=50):
    """Async function to fetch all assessment data in parallel"""
    
    # Create semaphore to limit concurrent requests
    semaphore = asyncio.Semaphore(max_concurrent)
    
    # Create aiohttp session with connection limits
    connector = aiohttp.TCPConnector(limit=100, limit_per_host=max_concurrent)
    timeout = aiohttp.ClientTimeout(total=30)
    
    async with aiohttp.ClientSession(connector=connector, timeout=timeout) as session:
        print(f"⚡ Creating {len(catalog_items)} assessment fetch tasks...")
        assessment_start = time.time()
        
        # Create all tasks
        tasks = []
        for item in catalog_items:
            task = fetch_assessment_data(
                session, 
                base_url, 
                token, 
                item.get('id'), 
                item.get('name', 'Unknown'),
                semaphore
            )
            tasks.append(task)
        
        # Execute all tasks with progress tracking
        print(f"🔄 Fetching assessment data (max {max_concurrent} concurrent)...")
        
        # Process in batches for progress reporting
        batch_size = 100
        results = []
        
        for i in range(0, len(tasks), batch_size):
            batch = tasks[i:i + batch_size]
            batch_results = await asyncio.gather(*batch, return_exceptions=True)
            results.extend(batch_results)
            
            completed = i + len(batch)
            progress = (completed / len(tasks)) * 100
            print(f"   📊 {completed:,}/{len(tasks):,} items processed ({progress:.1f}%)")
        
        assessment_time = time.time() - assessment_start
        return results, assessment_time

async def main_assessments():
    """Main async function to orchestrate the assessment data fetching"""
    start_time = time.time()
    
    # Configuration
    MAX_CONCURRENT_REQUESTS = 5  # Adjust
    base_url = os.getenv['customer_api_url']
    token = os.getenv('JWT_TOKEN')
    
    print("🚀 Starting assessment data fetching...")
    
    # Step 1: Fetch catalog (synchronous - no benefit from async here)
    catalog_start = time.time()
    catalog_items, page_count = fetch_catalog(base_url, token)
    catalog_time = time.time() - catalog_start
    print(f" ✅ {len(catalog_items)} items ({page_count} pages) in {catalog_time:.2f}s")
    
    # Step 2: Fetch assessment data (async for parallelization)
    results, assessment_time = await fetch_assessments_async(catalog_items, base_url, token, MAX_CONCURRENT_REQUESTS)
    
    total_time = time.time() - start_time
    
    # Step 3: Analyze results and generate summary
    print("\n📈 Analyzing results...")
    
    success_count = 0
    error_count = 0

    errors = []
    for result in results:
        if isinstance(result, Exception):
            error_count += 1
            continue
            
        status = result['status']
        if status == 'success':
            success_count += 1
        else:
            errors.append(f"Error for {result['item_name']} ({result['item_id'][:8]}): {status}")
            error_count += 1
    
    # Step 4: Display comprehensive summary
    print("\n" + "="*60)
    print("📊 ASSESSMENT DATA FETCH SUMMARY")
    print("="*60)
    print(f"⏱️  Total Time:           {total_time:.2f}s")
    print(f"📋 Catalog Fetch:        {catalog_time:.2f}s ({len(catalog_items):,} items, {page_count} pages)")
    print(f"⚡ Assessment Fetch:     {assessment_time:.2f}s")
    print(f"🔧 Max Concurrent:       {MAX_CONCURRENT_REQUESTS} requests")
    print()
    print("📈 RESULTS BREAKDOWN:")
    print(f"✅ Successful:           {success_count:,} items ({success_count/len(catalog_items)*100:.1f}%)")
    print(f"❌ Errors:               {error_count:,} items ({error_count/len(catalog_items)*100:.1f}%)")
    print(f"📊 Total Items:          {len(catalog_items):,}")
    print()
    print(f"⚡ Requests/Second:      {len(catalog_items)/assessment_time:.1f}")
    print("="*60)
    
    return results, errors

# Execute the main function using await (since Jupyter already has an event loop)
assessment_async_results, assessment_async_errors = await main_assessments()

What’s Next