Scheduled Deliveries API
Pick the commodity codes you care about, a cadence and a delivery time, and OilPriceAPI emails you the prices on that schedule with a CSV attached. Nothing to host, nothing to cron.
Available on every plan
- Free: 1 scheduled delivery
- Developer, Starter, Professional: 10 scheduled deliveries
- Scale and Enterprise: unlimited
Each delivery can carry up to 25 commodity codes. Deliveries do not count against your API request quota.
Overview
A scheduled delivery is a standing order. You create it once; we run it. Every delivery:
- serves each code through the same selection path as
GET /v1/prices/latest, so the email never disagrees with the API - includes the change versus the previous trading session in our data
- attaches the same rows as a CSV (
code, name, price, currency, unit, observed_at, previous_price, change, change_percent) - lists any code that had no current value under a "no current price" notice instead of failing the whole run
You can also manage deliveries without code from the dashboard at oilpriceapi.com/dashboard/deliveries.
Quick Start
Create a delivery
curl -X POST https://api.oilpriceapi.com/v1/scheduled-deliveries \
-H 'Authorization: Token YOUR_API_KEY' \
-H 'Content-Type: application/json' \
-d '{
"scheduled_delivery": {
"name": "Morning desk sheet",
"commodity_codes": ["BRENT_CRUDE_USD", "WTI_USD", "DUTCH_TTF_EUR", "EU_CARBON_EUR"],
"cadence": "weekdays",
"delivery_time": "07:00",
"time_zone": "Europe/London"
}
}'
Response 201 Created:
{
"data": {
"id": "6f1c2c0e-9b1e-4f7c-9a5e-3e6f1a2b3c4d",
"name": "Morning desk sheet",
"commodity_codes": ["BRENT_CRUDE_USD", "WTI_USD", "DUTCH_TTF_EUR", "EU_CARBON_EUR"],
"cadence": "weekdays",
"weekday": null,
"delivery_time": "07:00",
"time_zone": "Europe/London",
"destination": "email",
"recipient_email": "you@example.com",
"enabled": true,
"source": "api",
"deliveries_count": 0,
"last_delivered_on": null,
"next_delivery_at": "2026-09-28T06:00:00Z",
"last_run": null,
"created_at": "2026-09-25T15:04:11Z",
"updated_at": "2026-09-25T15:04:11Z"
},
"message": "Scheduled. First delivery 2026-09-28T06:00:00Z."
}
Send yourself a copy right now
Useful to check the codes and the format before the first scheduled run. One manual copy per delivery every 10 minutes.
curl -X POST https://api.oilpriceapi.com/v1/scheduled-deliveries/{id}/send_now \
-H 'Authorization: Token YOUR_API_KEY'
Response 202 Accepted:
{
"data": { "run_id": "…", "status": "sent", "recipient": "you@example.com", "error": null },
"message": "Sent to you@example.com."
}
Endpoints
| Method | Path | Purpose |
|---|---|---|
GET | /v1/scheduled-deliveries | List your deliveries and your plan limit |
POST | /v1/scheduled-deliveries | Create a delivery |
GET | /v1/scheduled-deliveries/{id} | Fetch one delivery, including its last run |
PATCH | /v1/scheduled-deliveries/{id} | Update codes, schedule, recipient, or pause/resume with enabled |
DELETE | /v1/scheduled-deliveries/{id} | Delete a delivery and its run history |
POST | /v1/scheduled-deliveries/{id}/send_now | Send one copy immediately |
All endpoints require an API key and are scoped to the key's account.
Fields
| Field | Type | Required | Notes |
|---|---|---|---|
commodity_codes | array of strings | yes | 1–25 codes from GET /v1/commodities. Case-insensitive; duplicates are removed. Unknown codes return 422 naming the code. |
cadence | string | no (default weekdays) | daily, weekdays (Mon–Fri), or weekly |
weekday | integer | for weekly | 1 = Monday … 7 = Sunday (default 1) |
delivery_time | string | no (default 07:00) | HH:MM, 24-hour, in time_zone |
time_zone | string | no (default UTC) | IANA zone, e.g. America/Chicago, Asia/Singapore |
name | string | no | Up to 120 characters; used in the subject line |
recipient_email | string | no | Defaults to the account email. One address per delivery. |
enabled | boolean | no (default true) | false pauses the delivery without deleting it |
Read-only fields on responses: deliveries_count, last_delivered_on, next_delivery_at (UTC ISO 8601, null while paused), last_run (trigger, status, sent_at, error).
Run status
Every scheduled or manual send leaves a run record, visible as last_run:
| Status | Meaning |
|---|---|
sent | Handed to the mail provider |
skipped | Nothing to send: the delivery was paused, or none of its codes had a current price. error says which. |
suppressed | Blocked by delivery safety (for example a hard-bounced address). error carries the reason. |
pending / processing | Queued or in flight; retried automatically |
Timing
Deliveries are dispatched every 15 minutes. A run for a given local date is created once the local clock in time_zone passes delivery_time, so a 07:00 delivery arrives between 07:00 and 07:15 local. Daylight-saving changes are handled by the zone, not by you.
The "change" column compares the served price with the last observation from an earlier calendar day. Over a weekend or holiday that is the previous trading session in our data.
Errors
| Status | error | When |
|---|---|---|
422 | validation_failed | Bad code, cadence, time, zone, or address; errors lists each problem |
422 | scheduled_delivery_limit_reached | Plan limit hit; response includes limit, current_count, upgrade_url |
404 | not_found | The id does not belong to this account |
429 | manual_send_cooldown | send_now called within 10 minutes of the last manual copy; honours Retry-After |
503 | scheduled_deliveries_disabled | Deliveries paused platform-wide during an incident |
Related
- Price Alerts — event-driven notifications when a price crosses a threshold
- Webhooks — push every price update to your own endpoint
- Latest prices — the endpoint each delivery row is selected through