Oil Price API Subscriptions API
Subscription lifecycle and billing state.
Subscription lifecycle and billing state.
Every API here is available over the APIs.io API and to AI agents over MCP.
One button, every client — Claude, Cursor, VS Code and the rest.
https://apis.io/mcp
find_apisBrowse and filter every API in the catalog.get_api_artifactsOne API's artifacts, grouped by type.get_openapiThe primary OpenAPI for this API.find_similar_apisAPIs that look like this one.apis_io_searchSTART HERE — APIs, providers and tags for one query, each with its total.resolveTurn a domain, URL or GitHub org into the provider it belongs to.find_cohortsEvery scored population of providers in the catalog.curl "https://apis.io/api/v1/apis/oilpriceapi:oilpriceapi-subscriptions-api"
curl "https://apis.io/api/v1/apis?limit=25"
Calls without X-API-Key need no key. Calls with it need a paid plan — get a key.
Free tier, no form to fill in. Signing in shares your email address with us — we store it to create your key and to recognise you if you sign in with another provider. See our Privacy Policy and Terms.
A second provider on the same verified email joins the account you already have.
openapi: 3.2.0
info:
title: Oil Price API V1 Subscriptions API
version: v1
description: 'Source-aware oil and energy data API. Check each endpoint''s source timestamp,
coverage, and freshness fields before using a response in a time-sensitive workflow. ## Authentication
API-key endpoints use `Authorization: Token YOUR_API_KEY`. Operations that do
not require an API key explicitly declare an empty security requirement. `/v1/demo/prices` is keyless. ## Rate Limiting
- Free tier: 50 requests per day
- Paid tiers have plan-specific monthly quotas. - `402` means the allowance is exhausted: a Free or post-trial caller
must upgrade, and a paid plan has used its monthly allowance (the body
carries `MONTHLY_QUOTA_EXCEEDED` and reset metadata, so do not retry before
the reset). `429` means an active-trial allowance or a rolling rate limit
(for example the hourly safety limit) is exceeded; retry `429` and transient
`5xx` responses with bounded backoff. - Website login and signup (`POST /sessions` and `POST /users`) also enforce
authentication-attempt limits independently of API plan quotas: 3 attempts
per normalized email address in 5 minutes, and 3 attempts per minute per
verified visitor IP. Requests without a verified proxy identity retain
the existing 20 requests/minute IP ceiling. A `429` on these routes means
wait for `Retry-After` before retrying; upgrading a plan does not remove
authentication-attempt limits. Futures curves are never refused because a market is closed or a session has
just reopened. The last available data is served with `metadata.freshness_status`,
`metadata.stale`, `metadata.latest_data_at` and `metadata.stale_by_minutes`:
`outside_market_hours` while the venue is closed (weekend, daily break,
holiday); `awaiting_first_print` for the first 30 minutes of a new session
while only a valid prior close exists; `degraded` when the market is open and
the source is behind. None of these is a fresh trade; source timestamps remain
unchanged. Data older than the 96-hour serving bound is refused with 503. Gasoil reports `metadata.freshness_status: no_recent_trades` when the
benchmark contract has not traded within 30 minutes but our collector confirmed
the quote within that window and the curve printed within 90 minutes. The last
trade is served with its true `latest_data_at` and `stale_by_minutes`, plus
`source_checked_at` and `curve_latest_trade_at`. Paid monthly-cap responses include `retry_guidance`: immediate retries cannot
restore quota. When a representative sample supports it, `batching_opportunity`
describes an estimated lower request rate; batching does not restore an already
exhausted allowance. It may replace an unnecessary upgrade recommendation. ## Continuous futures coverage
On `/v1/futures/continuous/brent`, `/v1/futures/continuous/wti` and their
`/historical` routes, `metadata.history_available_from` is the earliest
stored public continuous futures source date, falling back to collection
date for older imports without a source timestamp. It is a date string or
null when no public observations exist. Coverage metadata is cached for up
to one hour and does not promise observations or volume on every later date. It describes the archive independently of the request''s date range; account
depth limits remain in `metadata.history_window` on historical responses. Latest-response `metadata.total_historical_records` counts stored public
snapshots, including revisions, rather than distinct source dates. Latest
quotes and their prior-day comparisons exclude private organization rows
and other price types. Repeated source timestamps select the later collected
revision; the oldest metadata/response cache formats are no longer used. ## Spread and indicator query parameters
Spread and indicator endpoints reject unrecognized query parameter names with
HTTP 400 and `error.code: UNKNOWN_PARAMETERS`, after authentication and access
checks. The error includes `unrecognized_parameters`, `allowed_parameters`,
and a `suggestions` map for recognized date-name mistakes; values are not
echoed.'
contact:
name: Oil Price API Support
email: support@oilpriceapi.com
url: https://oilpriceapi.com
license:
name: Commercial License
url: https://oilpriceapi.com/terms
servers:
- url: https://api.oilpriceapi.com
description: Production server
tags:
- name: Subscriptions
description: Subscription lifecycle and billing state.
paths:
/v1/subscriptions:
get:
summary: List price watches
operationId: getV1Subscriptions
tags:
- Subscriptions
description: Persistent price "watches" for the authenticated user. Poll `/v1/subscriptions/events` for triggered events.
security:
- ApiKeyAuth: []
responses:
'200':
description: List of subscriptions
content:
application/json:
schema:
type: object
properties:
status:
type: string
example: success
data:
type: object
properties:
subscriptions:
type: array
items:
$ref: '#/components/schemas/PriceWatch'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
$ref: '#/components/responses/InternalServerError'
post:
summary: Create a price watch
operationId: postV1Subscriptions
tags:
- Subscriptions
description: Create a persistent price watch that emits events when its condition is met.
security:
- ApiKeyAuth: []
requestBody:
required: true
content:
application/json:
schema:
type: object
required:
- interval_seconds
properties:
name:
type: string
example: My WTI watch
codes:
type: array
description: Commodity codes to evaluate. Codes and minimum interval depend on the account tier.
minItems: 1
items:
type: string
example:
- WTI_USD
- BRENT_CRUDE_USD
code:
type: string
deprecated: true
description: Legacy single-code shorthand; folded into `codes`.
example: WTI_USD
interval_seconds:
type: integer
minimum: 1
description: Evaluation interval in seconds, subject to the account tier's minimum.
example: 3600
deliver_webhook:
type: boolean
default: false
example: true
source:
type: string
enum:
- api
- mcp
- dashboard
example: mcp
tool_name:
type: string
example: watch_commodity
responses:
'200':
description: Price watch created
content:
application/json:
schema:
$ref: '#/components/schemas/PriceWatchResponse'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'402':
description: Account watch count, code count, or interval entitlement requires an upgrade.
content:
application/json:
schema:
$ref: '#/components/schemas/PaymentRequiredResponse'
'422':
description: Price watch validation failed.
content:
application/json:
schema:
$ref: '#/components/schemas/FailErrorResponse'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/subscriptions/events:
get:
summary: Poll price watch events
operationId: getV1SubscriptionsEvents
tags:
- Subscriptions
description: 'Poll-based feed of events triggered by your price watches. Pass the response `cursor`
back as `since` to fetch only newer events. Polling this operation does not consume the
account''s monthly request quota and has its own rate-limit lane.'
security:
- ApiKeyAuth: []
parameters:
- name: since
in: query
required: false
description: Return events whose sequence is greater than this cursor. Defaults to 0.
schema:
type: integer
minimum: 0
default: 0
example: 1001
- name: limit
in: query
required: false
description: Maximum events to return. Values are clamped to 1 through 500.
schema:
type: integer
minimum: 1
maximum: 500
default: 100
example: 100
- name: watch_id
in: query
required: false
description: Restrict events to one price watch owned by the authenticated account.
schema:
type: string
example: 018f47a2-4d4d-7a44-9fa4-123456789abc
responses:
'200':
description: Events feed
content:
application/json:
schema:
type: object
properties:
status:
type: string
example: success
data:
type: object
properties:
cursor:
type: integer
example: 0
has_more:
type: boolean
example: false
events:
type: array
items:
type: object
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/subscriptions/{id}:
get:
summary: Get a price watch
operationId: getV1SubscriptionsById
description: Retrieve one price watch owned by the authenticated account.
tags:
- Subscriptions
security:
- ApiKeyAuth: []
parameters:
- name: id
in: path
required: true
description: Resource identifier.
schema:
type: string
example: 018f47a2-4d4d-7a44-9fa4-123456789abc
responses:
'200':
description: Subscription details
content:
application/json:
schema:
$ref: '#/components/schemas/PriceWatchResponse'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: Subscription not found (`{"error":{"code":"NOT_FOUND",...}}`).
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
$ref: '#/components/responses/InternalServerError'
patch:
summary: Update a price watch
operationId: patchV1SubscriptionsById
description: Update the configurable fields of one price watch owned by the authenticated account.
tags:
- Subscriptions
security:
- ApiKeyAuth: []
parameters:
- name: id
in: path
required: true
description: Resource identifier.
schema:
type: string
example: 018f47a2-4d4d-7a44-9fa4-123456789abc
requestBody:
content:
application/json:
schema:
type: object
properties:
name:
type: string
example: My energy watch
codes:
type: array
minItems: 1
items:
type: string
example:
- WTI_USD
code:
type: string
deprecated: true
description: Legacy single-code shorthand; folded into `codes`.
example: WTI_USD
interval_seconds:
type: integer
minimum: 1
description: Evaluation interval in seconds, subject to the account tier's minimum.
example: 900
deliver_webhook:
type: boolean
example: true
status:
type: string
enum:
- active
- paused
example: active
responses:
'200':
description: Subscription updated
content:
application/json:
schema:
$ref: '#/components/schemas/PriceWatchResponse'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: Subscription not found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'422':
description: Price watch validation failed.
content:
application/json:
schema:
$ref: '#/components/schemas/FailErrorResponse'
'500':
$ref: '#/components/responses/InternalServerError'
delete:
summary: Delete a price watch
operationId: deleteV1SubscriptionsById
description: Delete one price watch owned by the authenticated account.
tags:
- Subscriptions
security:
- ApiKeyAuth: []
parameters:
- name: id
in: path
required: true
description: Resource identifier.
schema:
type: string
example: 018f47a2-4d4d-7a44-9fa4-123456789abc
responses:
'204':
description: Subscription deleted; no response body is returned.
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: Subscription not found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/subscriptions/{id}/pause:
post:
summary: Pause a price watch
operationId: postV1SubscriptionsByIdPause
description: Pause evaluation of one price watch owned by the authenticated account.
tags:
- Subscriptions
security:
- ApiKeyAuth: []
parameters:
- name: id
in: path
required: true
description: Price watch identifier.
schema:
type: string
example: 018f47a2-4d4d-7a44-9fa4-123456789abc
responses:
'200':
description: Price watch paused
content:
application/json:
schema:
$ref: '#/components/schemas/PriceWatchResponse'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: Subscription not found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
$ref: '#/components/responses/InternalServerError'
/v1/subscriptions/{id}/resume:
post:
summary: Resume a price watch
operationId: postV1SubscriptionsByIdResume
description: Resume evaluation and immediately re-arm one price watch owned by the authenticated account.
tags:
- Subscriptions
security:
- ApiKeyAuth: []
parameters:
- name: id
in: path
required: true
description: Price watch identifier.
schema:
type: string
example: 018f47a2-4d4d-7a44-9fa4-123456789abc
responses:
'200':
description: Price watch resumed and re-armed
content:
application/json:
schema:
$ref: '#/components/schemas/PriceWatchResponse'
'401':
description: Unauthorized
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'404':
description: Subscription not found
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
'500':
$ref: '#/components/responses/InternalServerError'
components:
schemas:
PriceWatchResponse:
type: object
required:
- status
- data
properties:
status:
type: string
enum:
- success
example: success
data:
type: object
required:
- subscription
properties:
subscription:
$ref: '#/components/schemas/PriceWatch'
PriceWatch:
type: object
description: A persistent commodity watch evaluated on the configured interval.
required:
- id
- codes
- interval_seconds
- status
- deliver_webhook
- source
- created_at
properties:
id:
type: string
example: 018f47a2-4d4d-7a44-9fa4-123456789abc
name:
type:
- string
- 'null'
example: My WTI watch
codes:
type: array
minItems: 1
items:
type: string
example:
- WTI_USD
- BRENT_CRUDE_USD
interval_seconds:
type: integer
minimum: 1
example: 3600
status:
type: string
enum:
- active
- paused
example: active
deliver_webhook:
type: boolean
example: false
source:
type: string
enum:
- api
- mcp
- dashboard
example: api
tool_name:
type:
- string
- 'null'
example: watch_commodity
last_evaluated_at:
type:
- string
- 'null'
format: date-time
next_run_at:
type:
- string
- 'null'
format: date-time
created_at:
type: string
format: date-time
FailErrorResponse:
type: object
properties:
status:
type: string
example: fail
data:
type: object
properties:
error:
type: string
example: invalid_code
message:
type: string
example: Code 'NOT_REAL' not found. See /v1/commodities for all available codes.
suggestions:
type: array
items:
type: string
example: []
invalid_codes:
type: array
items:
type: string
example:
- NOT_REAL
PaymentRequiredResponse:
type: object
description: 'Returned when an allowance is exhausted: a free-tier or post-trial user has used
their request limit, or a paid plan has used its monthly allowance.
AI agents should interpret this as "upgrade or wait for the reset" (not "retry later").
Active-trial allowance and rolling rate limits return 429 Too Many Requests instead.
'
properties:
error:
type: string
example: Payment Required
error_code:
type: string
example: PAYMENT_REQUIRED
message:
type: string
example: You've used all 50 requests for Free tier today
current_usage:
type: object
properties:
used:
type: integer
example: 50
limit:
type: integer
example: 50
remaining:
type: integer
example: 0
tier:
type: string
example: free
upgrade_options:
type: array
items:
type: object
properties:
plan:
type: string
example: exploration
price:
type: integer
description: Price in cents (USD)
example: 1500
currency:
type: string
example: usd
requests:
type: integer
example: 10000
checkout_url:
type: string
example: https://www.oilpriceapi.com/pricing
docs:
type: string
example: https://docs.oilpriceapi.com/billing
ErrorResponse:
type: object
properties:
error:
type: object
properties:
code:
type: string
description: Machine-readable error code (e.g. UNAUTHORIZED, NOT_FOUND, INVALID_PARAMETER).
example: UNAUTHORIZED
message:
type: string
example: 'Missing or invalid API key. Include header: Authorization: Token YOUR_API_KEY'
status:
type: integer
example: 401
request_id:
type: string
example: fa0e389f-ca86-47b2-b167-2b4645a2fb8b
docs:
type: string
example: https://docs.oilpriceapi.com#UNAUTHORIZED
signup_url:
type:
- string
- 'null'
example: https://www.oilpriceapi.com/auth/signup
demo_endpoint:
type:
- string
- 'null'
example: /v1/demo/prices
responses:
InternalServerError:
description: Unexpected server error. Retry transient failures with bounded backoff and retain the request ID when contacting support.
content:
application/json:
schema:
$ref: '#/components/schemas/ErrorResponse'
securitySchemes:
ApiKeyAuth:
type: apiKey
in: header
name: Authorization
description: 'API key as: Authorization: Token YOUR_API_KEY'
externalDocs:
description: OilPriceAPI developer documentation
url: https://docs.oilpriceapi.com