---
title: "Get paginated historical price data"
method: GET
path: "/v1/prices/historical"
tags: ["Prices", "Premium"]
---

# Get paginated historical price data

`GET /v1/prices/historical`

## Request

- Base URL: `https://api.oilpriceapi.com`
- URL: `https://api.oilpriceapi.com/v1/prices/historical`
- Auth: API key in header `Authorization`

Retrieve the authenticated account's available history for one or more
commodity codes. `by_code` is required. The account's historical-depth
entitlement is enforced even when no dates are supplied.

The default interval is `daily`, and the default page size for aggregated
history is 500. Responses are paginated; follow `X-Page`, `X-Per-Page`,
`X-Total`, and `X-Total-Pages` rather than assuming one response contains
the entire archive. Use `interval=raw` only when individual collected rows
are required. Raw history defaults to 25 rows and applies a maximum of
100 per page. Raw responses expose `X-Page`, `X-Per-Page`, `X-Has-Next`
and credential-free next/previous `Link` headers, including cache hits.
`data.metadata.pagination` reports the effective page/per_page,
has_next, has_previous, truncated and per_page_clamped. Raw responses
do not compute a total count. Follow pages until has_next is false;
a later page alone still does not represent the entire requested window.
Raw history can include stored averages; use by_type=spot_price when
collected quote rows are required, and inspect instrument/time metadata.

Use `GET /v1/commodities/{code}` to discover links, source publication
cadence, and the requesting account's history entitlement.

Example:
```bash
curl "https://api.oilpriceapi.com/v1/prices/historical?by_code=JET_FUEL_USD&start_date=2025-01-01&interval=daily&per_page=500" \
  -H "Authorization: Token YOUR_API_KEY"
```

## Query parameters

- `by_code` string, required
- `by_type` string
- `by_source` string
- `start_date` string, date
- `end_date` string, date
- `past` string
- `interval` 'raw' | '1h' | 'hourly' | 'h' | '1d' | 'daily' | 'd' | 'day' | '1w' | 'weekly' | 'w' | 'week' | '1m' | 'monthly' | 'm' | 'month'
- `page` integer
- `per_page` integer

## Response `200`

Paginated historical price data available to the authenticated account.

- HistoricalPricesResponse
  - `status` 'success', required
  - `data` object, required
    - `prices` PriceData[], required
      - `benchmark` PriceBenchmark: Semantic identity and provenance for WTI or Brent benchmark prices. Explicit contract identity is included only when known. WTI retains its early-roll policy; the selected delivery month may differ from the earliest unexpired contract. Compare contract_month and observed_at when comparing quotes.
        - `id` string: Stable benchmark identifier
        - `name` string: Benchmark instrument name
        - `instrument_type` string
        - `quote_type` string
        - `unit` string
        - `contract_month` string: Selected delivery month when explicitly known; omitted for an unidentified publisher-managed contract
        - `roll_method` string: How the selected observation identifies its contract
        - `roll_signal` string: Stored signal used in contract selection, when available. A signal does not by itself mean a roll occurred.
        - `observed_at` string, date-time, nullable: Source market observation time; null when unavailable, never substituted with collection time
        - `observation_time_basis` 'upstream_market' | 'unavailable'
        - `collected_at` string, date-time: Time the selected price row was collected
        - `source_tier` string: Selection tier of the observation
      - `price` number, float
      - `formatted` string
      - `currency` string: ISO currency code for priced series (`USD`, `EUR`, ...). Index-type codes return `INDEX` instead: the value is an index level, not a money amount, and `formatted` carries no currency symbol (`"4340.00"`). Index-type codes are `BALTIC_DRY_INDEX`, `BALTIC_CAPESIZE_INDEX`, `BALTIC_PANAMAX_INDEX`, `BALTIC_SUPRAMAX_INDEX` and `SCFI_INDEX`, all with `unit: index`. This measure and formatting also apply to daily, weekly and monthly aggregates from the historical and past-window endpoints. The Drewry World Container Index composite (`CONTAINER_FREIGHT_COMPOSITE_USD`) and its lanes are priced in dollars per 40ft container and return `USD` with `unit: usd_per_40ft`.
      - `code` string
      - `requested_code` string: Legacy alias requested on latest-price reads when a different code is served; omitted when the batch also requests that served code.
      - `alias_of` string: The served series code corresponding to requested_code on latest-price reads.
      - `contract_month` string, nullable: For WTI_USD and BRENT_CRUDE_USD benchmark quotes, the selected backing row's delivery month (YYYY-MM), matching benchmark.contract_month when known. Null when the publisher-managed quote does not identify a month. This identifies the contract served; it does not necessarily mean the nearest unexpired contract, because a benchmark may roll before expiry.
      - `created_at` string, date-time
      - `updated_at` string, date-time
      - `type` string
      - `unit` string: Pricing unit (`barrel`, `gallon`, `mmbtu`, `usd_per_tonne`, ...). `index` for the index-type codes listed under `currency`; `usd_per_40ft` for the Drewry World Container Index composite and lane codes.
      - `source` string: Customer-safe source label. Non-government market sources are returned as `market_reporting`.
      - `observed_at` string, date-time, nullable: Validated source observation timestamp when known; null when no genuine source timestamp is available.
      - `source_date` string, date: Source observation date when available.
      - `as_of` string, date-time: Timestamp when the value was last a genuine source observation.
      - `collected_at` string, date-time: Timestamp when OilPriceAPI collected or produced this row.
      - `ingested_at` string, date-time, nullable: Collection timestamp for an individual raw observation; null for aggregated averages, which have no single ingestion instant.
      - `stale` boolean: Whether `as_of` exceeds this series' expected publication cadence.
      - `age_days` integer: Whole days between `as_of` and response generation.
      - `synthetic` boolean: True only for carried-forward heartbeat rows rather than genuine source observations.
      - `price_type` string: Underlying price type for aggregated historical rows.
      - `data_status` 'current' | 'delayed' | 'stale' | 'degraded' | 'provisional': Freshness classification of the price, derived from the source session (`observed_at`) against the series' expected publication cadence (`freshness.expected_max_age_seconds`), not from the time OilPriceAPI collected the row. - `current`: `freshness.age_seconds` is within `expected_max_age_seconds`. - `delayed`: past the expected cadence but inside the stale limit; the last print is still served. - `stale`: past the stale limit; `stale` is `true`. - `degraded`: the series' data-gap circuit breaker is open (`freshness.circuit_breaker_open: true`) while the value is not yet stale. - `provisional`: the served print is held from a single source and has not yet been confirmed by a second independent source. Reserved for the Baltic session-ledger codes (`BALTIC_DRY_INDEX`, `BALTIC_CAPESIZE_INDEX`, `BALTIC_PANAMAX_INDEX`, `BALTIC_SUPRAMAX_INDEX`); not served as of 2026-10-10. Index-type codes print once per London business day (Baltic indices) or once a week (`SCFI_INDEX` on Friday, `CONTAINER_FREIGHT_COMPOSITE_USD` on Thursday), so `delayed` between prints is the normal state for them, not an error. To tell "old because weekly" from "old because broken", compare `freshness.age_seconds` with `freshness.expected_max_age_seconds`. Live example (2026-10-10, `/v1/prices/latest?by_code=BALTIC_CAPESIZE_INDEX`): `price: 4340.0`, `formatted: "4340.00"`, `currency: "INDEX"`, `unit: "index"`, `observed_at: "2026-10-09T12:00:00.000Z"`, `data_status: "current"`, `freshness: {status: "current", age_seconds: 81475, expected_max_age_seconds: 324000}`.
      - `freshness` object
        - `status` string
        - `age_seconds` integer
        - `expected_max_age_seconds` integer
      - `changes` object: Price change over rolling windows. Keys are 24h/7d/30d/90d.
        - `24h` PriceChange
          - `amount` number, float
          - `percent` number, float
          - `previous_price` number, float
        - `7d` PriceChange
          - `amount` number, float
          - `percent` number, float
          - `previous_price` number, float
        - `30d` PriceChange
          - `amount` number, float
          - `percent` number, float
          - `previous_price` number, float
        - `90d` PriceChange
          - `amount` number, float
          - `percent` number, float
          - `previous_price` number, float
    - `metadata` object: Present for bounded or relative windows; describes the applied interval, entitlement-aware window, pagination, and availability.
    - `missing` object[]: Requested commodity codes with no matching data in the applied window.

## Other responses

- `400`: Missing or invalid code, date, interval, or relative window.
- `401`: Unauthorized - Invalid API key
- `402`: Payment required - Paid subscription needed for historical data
- `429`: Rolling rate limit exceeded. Retry with bounded backoff.
- `500`: Unexpected server error. Retry transient failures with bounded backoff and retain the request ID when contacting support.

## Changes

- **2026-10-20** `dc3f91f8926d`: 2 info
  - added the optional property `data/prices/items/alias_of` to the response with the `200` status
  - added the optional property `data/prices/items/requested_code` to the response with the `200` status
- **2026-10-20** `b987321524a8`: 1 breaking, 3 info
  - the response property `data/prices/items/observed_at` became nullable for the status `200`
  - added the optional property `data/prices/items/benchmark` to the response with the `200` status
  - added the optional property `data/prices/items/contract_month` to the response with the `200` status
  - added the optional property `data/prices/items/ingested_at` to the response with the `200` status
- **2026-10-11** `84bf31be62ff`: 5 warning
  - added the new `current` enum value to the `data/prices/items/data_status` response property for the response status `200`
  - added the new `degraded` enum value to the `data/prices/items/data_status` response property for the response status `200`
  - added the new `delayed` enum value to the `data/prices/items/data_status` response property for the response status `200`
  - added the new `provisional` enum value to the `data/prices/items/data_status` response property for the response status `200`
  - …1 more

[Operation history](https://skmtc.dev/oilpriceapi/apis/oil-price-api-v1/changes/v1/prices/historical/get.md)

---

[API](https://skmtc.dev/oilpriceapi/apis/oil-price-api-v1.md) - [All operations](https://skmtc.dev/oilpriceapi/apis/oil-price-api-v1/llms.txt) - [OpenAPI document](https://skmtc.dev/oilpriceapi/apis/oil-price-api-v1/revisions/7f8b4d763210?raw)
