Demo API (No Authentication)
Status: ✅ Available | ⚡ Real-time | 🆓 No API Key Required
Try the API instantly without signing up. Perfect for testing and exploring.
Overview
The demo endpoint provides instant access to real-time commodity prices without authentication. Use it to:
- Test your integration before signing up
- Explore API responses in the browser
- Build quick prototypes
Limitations
| Feature | Demo API | Authenticated API |
|---|---|---|
| Rate Limit | 25/day per IP (see the three lanes below) | Up to 100k+/month |
| Codes per call | 20 (?codes=A,B,C, billed as one request) | 20 |
| Commodities | 37 free-tier codes | 220+ |
| Historical Data | ❌ | ✅ |
| WebSocket | ❌ | ✅ (Premium) |
Three limits apply, in this order
All three are keyed on your IP address. The first one you exceed is the one that refuses you, and the two lanes return different 429 bodies — branch on the body, not on the status.
| Lane | Limit | Enforced by | 429 shape |
|---|---|---|---|
| Per-minute burst | 10 requests/minute for keyless /v1/* requests — plan against this one | Request middleware, before the demo endpoint runs | Top-level error_code: "RATE_LIMIT_EXCEEDED", no demo fields, no X-Demo-Mode. Retry-After is the seconds left in the minute, and X-RateLimit-Limit reports whichever lane tripped (see below) |
| Hourly burst guard | 30 requests/hour | The demo endpoint | error.code: "DEMO_RATE_LIMIT_EXCEEDED" (below) |
| Daily cap (binding) | 25 requests/day | The demo endpoint | error.code: "DEMO_RATE_LIMIT_EXCEEDED" (below) |
The daily cap of 25 is the binding one by design — 30/hour is only a burst guard above it. The per-minute middleware lane is not demo-specific: it applies to every keyless request, so a tight loop trips it long before the hourly limit, and the response carries none of the demo's advice fields.
Treat the per-minute headers as "whichever lane tripped", not as a fixed number. There are two middleware rules and a keyless /v1 request matches both: a 10/minute rule for keyless /v1/* and a broader 20/minute per-IP rule for keyless traffic generally. The first rule you exceed is the one that refuses you, so requests 11–20 within a minute report X-RateLimit-Limit: 10 while a caller already past 20 reports 20 (observed live on 2026-09-11 from a deep burst). 10 requests/minute is the ceiling to design against.
The demo tier exists for evaluation; anything that needs more than 25 calls a day wants the free API key, which allows 50 requests per day and moves you onto the much more generous authenticated lanes.
Available Commodities
37 free-tier codes are available without authentication. A call with no parameters returns the first 20 of them (the per-request batch cap); name the ones you want with ?codes=A,B,C — up to 20 codes in one call, counted as a single request:
BRENT_CRUDE_USD, WTI_USD, NATURAL_GAS_USD, GOLD_USD, EUR_USD, GBP_USD, HEATING_OIL_USD, GASOLINE_USD, DIESEL_USD, OPEC_BASKET_USD, DUBAI_CRUDE_USD, BASRAH_MEDIUM_USD, BASRAH_HEAVY_USD, AZERI_LIGHT_USD, URALS_CRUDE_USD, WCS_CRUDE_USD, GASOLINE_RBOB_USD, ULSD_DIESEL_USD, DIESEL_RETAIL_USD, SILVER_USD, GOLD_AM_USD, GOLD_PM_USD, SILVER_FIX_USD, PLATINUM_USD, PALLADIUM_USD, USD_NOK, EUR_NOK, NATURAL_GAS_GBP, COAL_USD, NEWCASTLE_COAL_USD, COKING_COAL_USD, CAPP_COAL_USD, PRB_COAL_USD, ILLINOIS_COAL_USD, EU_CARBON_EUR, UK_CARBON_GBP, PROPANE_MONT_BELVIEU_USD
Requesting more than 20 codes in one call returns 400 with "Too many commodity codes requested (max: 20, requested: N)" — the same cap the authenticated API enforces. Premium codes (gas hubs, JKM, jet fuel, futures) are ignored in batch calls and return 403 PREMIUM_COMMODITY on the single-code endpoint.
Response
{
"status": "success",
"data": {
"prices": [
{
"code": "NATURAL_GAS_USD",
"name": "Natural Gas (Henry Hub)",
"price": 5.34,
"currency": "USD",
"updated_at": "2026-01-22T13:43:16Z",
"change_24h": 7.04,
"source": "OilPriceAPI"
}
],
"meta": {
"demo_mode": true,
"rate_limit": "30 requests per hour and 25 per day, per IP",
"rate_limits": {
"per_hour": 30,
"per_day": 25,
"scope": "per IP address, no API key required"
},
"max_codes_per_request": 20
}
}
}
Response Fields
| Field | Type | Description |
|---|---|---|
code | string | Commodity identifier |
name | string | Human-readable commodity name |
price | number | Current spot price |
currency | string | Price currency (usually USD) |
updated_at | string | ISO 8601 timestamp of last update |
change_24h | number | Percentage change from 24 hours ago (e.g., 7.04 = +7.04%) |
source | string | Always "OilPriceAPI" for demo |
change_24h is a percentage
The change_24h field contains the percentage change from 24 hours ago, not the absolute dollar change. For example, 7.04 means the price increased by 7.04% in the last 24 hours.
Examples
# Get all demo prices
curl https://api.oilpriceapi.com/v1/demo/prices
# Get specific commodity (demo only supports free-tier commodities)
curl https://api.oilpriceapi.com/v1/demo/prices/WTI_USD
// JavaScript - No API key needed!
const response = await fetch("https://api.oilpriceapi.com/v1/demo/prices");
const data = await response.json();
data.data.prices.forEach((commodity) => {
const changeDir = commodity.change_24h >= 0 ? "↑" : "↓";
console.log(
`${commodity.name}: $${commodity.price} ${changeDir}${Math.abs(commodity.change_24h)}%`,
);
});
# Python - No API key needed!
import requests
response = requests.get('https://api.oilpriceapi.com/v1/demo/prices')
data = response.json()
for commodity in data['data']['prices']:
change_dir = '↑' if commodity['change_24h'] >= 0 else '↓'
print(f"{commodity['name']}: ${commodity['price']} {change_dir}{abs(commodity['change_24h'])}%")
Rate Limit Headers
A demo response served by the endpoint — that is, one that got past the per-minute middleware lane — carries both demo windows (header names verified against a live response, 2026-09-11; HTTP/2 delivers them lowercase):
x-ratelimit-limit: 30
x-ratelimit-remaining: 12
x-ratelimit-reset: 1789167599
x-ratelimit-limit-hour: 30
x-ratelimit-remaining-hour: 12
x-ratelimit-limit-day: 25
x-ratelimit-remaining-day: 4
x-ratelimit-reset-day: 1789171199
x-demo-mode: true
X-RateLimit-Remaining (the standard header) always reports the binding allowance — the smaller of the hourly and daily remainders — so a client that reads only the standard header is never told it has calls left that it does not have. On these responses X-RateLimit-Limit echoes the hourly limit (30); read X-RateLimit-Limit-Day / X-RateLimit-Remaining-Day for the binding daily window. A 429 from the per-minute middleware lane sets X-RateLimit-Limit to whichever rule tripped (10 or 20, as above) and sends none of the -Hour / -Day headers, so treat the presence of X-Demo-Mode as the signal that you are reading demo counters.
Rate Limit Exceeded (429)
When the hourly or daily demo window is exhausted, the response is 429 with Retry-After set and a machine-readable body. This is an abridged capture of a real 2026-09-11 response — the plans block, the rest of what_to_do, and the UTM parameters on the URLs are omitted for length:
{
"status": "fail",
"error": {
"code": "DEMO_RATE_LIMIT_EXCEEDED",
"message": "Demo limit reached: 25 requests per day from this IP (the hourly burst limit is 30). No API key is needed to use the demo — this limit exists so it stays available to everyone.",
"limits": {
"per_hour": 30,
"per_day": 25,
"scope": "per IP address, no API key required"
},
"retry_after": 5560,
"what_to_do": [
"Poll less often. Measured on our own price history: WTI_USD changes about every 24 minutes. Asking more frequently than that returns the value you already have.",
"Batch your codes. Up to 20 codes in one request count as a SINGLE request, on the demo and on the authenticated API alike, so you never need one request per code. …"
],
"free_api_key": {
"requests_per_day": 50,
"cost": "free, no card required",
"signup_url": "https://www.oilpriceapi.com/signup"
},
"documentation_url": "https://docs.oilpriceapi.com"
}
}
The body also carries update_frequency (measured change cadence for the codes you asked for) and a plans block sized from your IP's observed volume. The 429 sets Cache-Control: no-store and X-RateLimit-Remaining: 0; honor Retry-After before retrying.
A 429 from the per-minute middleware lane looks nothing like this — it has no status or error.code, carrying instead a top-level error: "Rate limit exceeded" with error_code: "RATE_LIMIT_EXCEEDED", a retry_after of at most 60 seconds, and an upgrade block. If you are retrying automatically, read error_code first: a RATE_LIMIT_EXCEEDED clears within the minute, while DEMO_RATE_LIMIT_EXCEEDED may not clear until tomorrow.
Ready for More?
Sign up for free to access:
- Broad commodity catalog; exact access varies by dataset and plan
- Historical data
- Higher rate limits
- WebSocket streaming