curl -X POST "https://api.dcycle.io/v2/vehicle-consumptions" \
-H "x-api-key: ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "Idempotency-Key: 6a5b4c3d-2e1f-4a0b-9c8d-7e6f5a4b3c2d" \
-H "Content-Type: application/json" \
-d '{
"records": [
{
"vehicle_id": "8c7b6a59-4d3e-4f21-9a0b-1c2d3e4f5a6b",
"quantity": 52.4,
"unit_id": "54a709bf-79fe-4acb-ab85-5e1d7cd26eb4",
"start_date": "2026-03-01",
"end_date": "2026-03-31",
"total_spend": 81.75,
"currency_unit_id": "d3e37f2b-0fc3-4532-82f8-3890ab56ad37",
"custom_id": "FUELCARD-2026-03-0042",
"client_row_id": "line-42"
},
{
"vehicle_id": "8c7b6a59-4d3e-4f21-9a0b-1c2d3e4f5a6b",
"quantity": 85.9,
"unit_id": "d3e37f2b-0fc3-4532-82f8-3890ab56ad37",
"start_date": "2026-04-01",
"end_date": "2026-04-30"
}
]
}'
import os
import uuid
import requests
response = requests.post(
"https://api.dcycle.io/v2/vehicle-consumptions",
headers={
"x-api-key": os.environ["DCYCLE_API_KEY"],
"x-organization-id": os.environ["DCYCLE_ORG_ID"],
"Idempotency-Key": str(uuid.uuid4()), # keep it to retry this same batch
},
json={
"records": [
{
"vehicle_id": "8c7b6a59-4d3e-4f21-9a0b-1c2d3e4f5a6b",
"quantity": 52.4,
"unit_id": "54a709bf-79fe-4acb-ab85-5e1d7cd26eb4", # litres
"start_date": "2026-03-01",
"end_date": "2026-03-31",
"client_row_id": "line-42",
}
]
},
timeout=60,
)
response.raise_for_status()
job = response.json()
print(job["id"], job["status"])
{
"id": "2b3c4d5e-6f70-4a81-9b2c-3d4e5f607182",
"status": "processing",
"entity_type": "vehicle_consumptions",
"operation": "create",
"source": "api_bulk",
"counts": { "submitted": 2, "succeeded": 0, "failed": 0 },
"chunks_total": 1,
"chunks_done": 0,
"created_at": "2026-10-19T09:12:44.031Z",
"finished_at": null,
"links": {
"self": "/v2/ingest-jobs/2b3c4d5e-6f70-4a81-9b2c-3d4e5f607182",
"items": "/v2/ingest-jobs/2b3c4d5e-6f70-4a81-9b2c-3d4e5f607182/items"
}
}
{
"type": "https://code.dcycle.io/api-reference/errors#BULK_RECORDS_REJECTED",
"title": "Unprocessable Entity",
"status": 422,
"code": "BULK_RECORDS_REJECTED",
"detail": "1 of the submitted records are invalid; nothing was written.",
"errors": [
{
"source_row_index": 0,
"client_row_id": "line-42",
"error_code": "UNIT_NOT_ALLOWED_FOR_FUEL",
"error_params": { "field": "unit_id", "value": "accd2a4d-ff0c-4e7c-8e17-a187b01e49b4" }
}
],
"rejected": 1,
"request_id": "b2248a31-0f1b-4a7d-83ce-b29bd5977b4c"
}
{ "dry_run": true, "submitted": 2, "valid": 2 }
Create Vehicle Consumptions (v2)
Create up to 5,000 vehicle consumptions in one request; emissions are calculated asynchronously
POST
/
v2
/
vehicle-consumptions
curl -X POST "https://api.dcycle.io/v2/vehicle-consumptions" \
-H "x-api-key: ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "Idempotency-Key: 6a5b4c3d-2e1f-4a0b-9c8d-7e6f5a4b3c2d" \
-H "Content-Type: application/json" \
-d '{
"records": [
{
"vehicle_id": "8c7b6a59-4d3e-4f21-9a0b-1c2d3e4f5a6b",
"quantity": 52.4,
"unit_id": "54a709bf-79fe-4acb-ab85-5e1d7cd26eb4",
"start_date": "2026-03-01",
"end_date": "2026-03-31",
"total_spend": 81.75,
"currency_unit_id": "d3e37f2b-0fc3-4532-82f8-3890ab56ad37",
"custom_id": "FUELCARD-2026-03-0042",
"client_row_id": "line-42"
},
{
"vehicle_id": "8c7b6a59-4d3e-4f21-9a0b-1c2d3e4f5a6b",
"quantity": 85.9,
"unit_id": "d3e37f2b-0fc3-4532-82f8-3890ab56ad37",
"start_date": "2026-04-01",
"end_date": "2026-04-30"
}
]
}'
import os
import uuid
import requests
response = requests.post(
"https://api.dcycle.io/v2/vehicle-consumptions",
headers={
"x-api-key": os.environ["DCYCLE_API_KEY"],
"x-organization-id": os.environ["DCYCLE_ORG_ID"],
"Idempotency-Key": str(uuid.uuid4()), # keep it to retry this same batch
},
json={
"records": [
{
"vehicle_id": "8c7b6a59-4d3e-4f21-9a0b-1c2d3e4f5a6b",
"quantity": 52.4,
"unit_id": "54a709bf-79fe-4acb-ab85-5e1d7cd26eb4", # litres
"start_date": "2026-03-01",
"end_date": "2026-03-31",
"client_row_id": "line-42",
}
]
},
timeout=60,
)
response.raise_for_status()
job = response.json()
print(job["id"], job["status"])
{
"id": "2b3c4d5e-6f70-4a81-9b2c-3d4e5f607182",
"status": "processing",
"entity_type": "vehicle_consumptions",
"operation": "create",
"source": "api_bulk",
"counts": { "submitted": 2, "succeeded": 0, "failed": 0 },
"chunks_total": 1,
"chunks_done": 0,
"created_at": "2026-10-19T09:12:44.031Z",
"finished_at": null,
"links": {
"self": "/v2/ingest-jobs/2b3c4d5e-6f70-4a81-9b2c-3d4e5f607182",
"items": "/v2/ingest-jobs/2b3c4d5e-6f70-4a81-9b2c-3d4e5f607182/items"
}
}
{
"type": "https://code.dcycle.io/api-reference/errors#BULK_RECORDS_REJECTED",
"title": "Unprocessable Entity",
"status": 422,
"code": "BULK_RECORDS_REJECTED",
"detail": "1 of the submitted records are invalid; nothing was written.",
"errors": [
{
"source_row_index": 0,
"client_row_id": "line-42",
"error_code": "UNIT_NOT_ALLOWED_FOR_FUEL",
"error_params": { "field": "unit_id", "value": "accd2a4d-ff0c-4e7c-8e17-a187b01e49b4" }
}
],
"rejected": 1,
"request_id": "b2248a31-0f1b-4a7d-83ce-b29bd5977b4c"
}
{ "dry_run": true, "submitted": 2, "valid": 2 }
← Vehicles API
Create one or many fuel or energy consumptions of your vehicles (Scope 1 for owned vehicles, with their upstream
emissions in Scope 3 category 3). Say which vehicle, how much, in which unit and over which period; Dcycle picks the
emission factor of the vehicle’s class, fuel and country and calculates the emissions in the background. The
consumptions are the ones the app shows under each vehicle.
The request checks every record and writes them all at once, then answers
Request-level errors:
202 Accepted with an
ingest job that calculates them. Follow it with
GET /v2/ingest-jobs/{id} or the
ingest_job.finished webhook.
Beta. Part of the bulk ingest API, currently in beta. The contract may still change before general availability.
curl -X POST "https://api.dcycle.io/v2/vehicle-consumptions" \
-H "x-api-key: ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "Idempotency-Key: 6a5b4c3d-2e1f-4a0b-9c8d-7e6f5a4b3c2d" \
-H "Content-Type: application/json" \
-d '{
"records": [
{
"vehicle_id": "8c7b6a59-4d3e-4f21-9a0b-1c2d3e4f5a6b",
"quantity": 52.4,
"unit_id": "54a709bf-79fe-4acb-ab85-5e1d7cd26eb4",
"start_date": "2026-03-01",
"end_date": "2026-03-31",
"total_spend": 81.75,
"currency_unit_id": "d3e37f2b-0fc3-4532-82f8-3890ab56ad37",
"custom_id": "FUELCARD-2026-03-0042",
"client_row_id": "line-42"
},
{
"vehicle_id": "8c7b6a59-4d3e-4f21-9a0b-1c2d3e4f5a6b",
"quantity": 85.9,
"unit_id": "d3e37f2b-0fc3-4532-82f8-3890ab56ad37",
"start_date": "2026-04-01",
"end_date": "2026-04-30"
}
]
}'
import os
import uuid
import requests
response = requests.post(
"https://api.dcycle.io/v2/vehicle-consumptions",
headers={
"x-api-key": os.environ["DCYCLE_API_KEY"],
"x-organization-id": os.environ["DCYCLE_ORG_ID"],
"Idempotency-Key": str(uuid.uuid4()), # keep it to retry this same batch
},
json={
"records": [
{
"vehicle_id": "8c7b6a59-4d3e-4f21-9a0b-1c2d3e4f5a6b",
"quantity": 52.4,
"unit_id": "54a709bf-79fe-4acb-ab85-5e1d7cd26eb4", # litres
"start_date": "2026-03-01",
"end_date": "2026-03-31",
"client_row_id": "line-42",
}
]
},
timeout=60,
)
response.raise_for_status()
job = response.json()
print(job["id"], job["status"])
{
"id": "2b3c4d5e-6f70-4a81-9b2c-3d4e5f607182",
"status": "processing",
"entity_type": "vehicle_consumptions",
"operation": "create",
"source": "api_bulk",
"counts": { "submitted": 2, "succeeded": 0, "failed": 0 },
"chunks_total": 1,
"chunks_done": 0,
"created_at": "2026-10-19T09:12:44.031Z",
"finished_at": null,
"links": {
"self": "/v2/ingest-jobs/2b3c4d5e-6f70-4a81-9b2c-3d4e5f607182",
"items": "/v2/ingest-jobs/2b3c4d5e-6f70-4a81-9b2c-3d4e5f607182/items"
}
}
{
"type": "https://code.dcycle.io/api-reference/errors#BULK_RECORDS_REJECTED",
"title": "Unprocessable Entity",
"status": 422,
"code": "BULK_RECORDS_REJECTED",
"detail": "1 of the submitted records are invalid; nothing was written.",
"errors": [
{
"source_row_index": 0,
"client_row_id": "line-42",
"error_code": "UNIT_NOT_ALLOWED_FOR_FUEL",
"error_params": { "field": "unit_id", "value": "accd2a4d-ff0c-4e7c-8e17-a187b01e49b4" }
}
],
"rejected": 1,
"request_id": "b2248a31-0f1b-4a7d-83ce-b29bd5977b4c"
}
{ "dry_run": true, "submitted": 2, "valid": 2 }
How it works
- Every record is checked. If any is invalid the whole request is rejected with
422and nothing is written; the answer lists every invalid record, so you can fix them all at once. - The consumptions are written at once, with status
loading. Each belongs to its vehicle’s organization: thex-organization-idorganization or one under it that you can write to. - The job calculates them in the background, in chunks. Each consumption ends
successorerror: its job item is thenfailedwithCALCULATION_FAILED, e.g. when there is no emission factor for the vehicle’s class, fuel and country in the consumption’s year. - When every chunk is done the job is
completed(orcompleted_with_errors) andingest_job.finishedis sent to your webhook endpoints.
A spend in euros
A consumption in euros (unit_id d3e37f2b-0fc3-4532-82f8-3890ab56ad37) is stored in litres: the amount is divided
by the pump price, taxes included, of the vehicle’s fuel in the vehicle’s country in the EU Weekly Oil Bulletin
published on or up to two weeks before start_date. The euros are kept as the consumption’s spend. A fuel or country
the bulletin does not cover, or a date with no bulletin (before 2020), is NO_FUEL_PRICE_FOR_SPEND: send litres
instead. Vehicles with a legacy custom emission factor group keep the euros, since their factors may be per euro.
Request
Headers
string
required
Your API key (see Authentication).
string
required
Your organization. Its vehicles, and those of the organizations under it that you can write to, can take
consumptions; each consumption belongs to its vehicle’s organization.
string
required
1 to 255 characters, unique per batch. A retry with the same key and body returns the original job (with
Idempotent-Replayed: true) instead of creating the consumptions again; the same key with another body is
422 IDEMPOTENCY_KEY_REUSED, and while the first request is still being processed 409 IDEMPOTENCY_KEY_IN_PROGRESS.Query Parameters
boolean
default:"false"
Check only: the same
422 for invalid records, nothing written, no job; 200 with
{"dry_run": true, "submitted": n, "valid": n} when every record is valid.Body Parameters
array[object]
required
The consumptions to create, 1 to 5,000. A field not listed here is rejected (
SCHEMA_INVALID).Show record
Show record
uuid
required
The vehicle: an
id from Create Vehicles (v2) or
List Vehicles. It must not be archived.number
required
How much was consumed, in
unit_id: 0 or more, up to 3 decimals.uuid
required
The unit of
quantity: one of the fuel’s fuel_units in List Vehicle Fuels
(litres 54a709bf-79fe-4acb-ab85-5e1d7cd26eb4, kilograms, kWh, kilometres…), or euros (see
A spend in euros).date
required
First day of the period consumed,
YYYY-MM-DD, from 1970 to next year. Its year picks the factor’s year.date
required
Last day of the period consumed, on or after
start_date.uuid
Which of the vehicle’s fuels was consumed, for a vehicle with several. Omitted: the vehicle’s primary fuel.
number
What the consumption cost, in
currency_unit_id: 0 or more. Informative, it does not change the emissions. Send
it with currency_unit_id, and not with a quantity in euros (that quantity already is the spend).uuid
The currency of
total_spend: a unit of GET /v2/units whose type is
fiat_currency, e.g. euros d3e37f2b-0fc3-4532-82f8-3890ab56ad37.string
Your own reference for the consumption (a fuel card line, an invoice number), up to 255 characters. Stored with
it and shown in the app.
string
Your own reference for the record, up to 255 characters, echoed in errors and in the job’s items.
Response
202 Accepted with the job (entity_type: vehicle_consumptions, operation: create) and a Location header to it.
GET /v2/ingest-jobs/{id}/items has one item per record, in the order sent:
its entity_id is the consumption’s id (the one Delete Vehicle Consumptions (v2)
takes), and a failed item carries its error_code.
Errors
Problem details. Per-record codes inerrors[].error_code of a 422 BULK_RECORDS_REJECTED:
error_code | Cause |
|---|---|
SCHEMA_INVALID | The record does not match the schema: a missing or unknown field (e.g. organization_id), a negative quantity or one with more than 3 decimals, a date out of range, end_date before start_date, total_spend without currency_unit_id or with a quantity in euros… error_params.errors says where. |
REFERENCE_NOT_FOUND | vehicle_id does not exist, is deleted or is outside your organization’s perimeter (all answer the same), or unit_id is not a unit. error_params.field says which. |
VEHICLE_ARCHIVED | The vehicle is archived: restore it in the app first. |
FUEL_NOT_IN_VEHICLE | vehicle_fuel_id is not one of the vehicle’s fuels. |
UNIT_NOT_ALLOWED_FOR_FUEL | unit_id is not one of the fuel’s units. |
UNIT_NOT_SUPPORTED | currency_unit_id is not a currency. |
NO_FUEL_PRICE_FOR_SPEND | A consumption in euros with no bulletin price for the vehicle’s country and fuel on start_date. |
401 for a missing or invalid API key, 403 LOGGED_USER_NOT_MEMBER when the key’s user is not a
member of the organization, 422 REQUEST_VALIDATION_FAILED for a malformed body (no records, more than 5,000, no
Idempotency-Key), 422 IDEMPOTENCY_KEY_REUSED, 409 IDEMPOTENCY_KEY_IN_PROGRESS, and 429 beyond the
rate limits.
Limits
| Limit | Value |
|---|---|
| Records per request | 1 to 5,000 |
| Consumptions per calculation chunk | 200 |
Related Endpoints
Delete Vehicle Consumptions (v2)
Delete consumptions by id
Create Vehicles (v2)
The vehicles, and their ids
List Vehicle Fuels
Fuel and unit ids
Get Ingest Job
Follow the calculation
List Job Items
The consumption id and outcome of each record
Was this page helpful?