> ## Documentation Index
> Fetch the complete documentation index at: https://code.dcycle.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Create Vehicle Consumptions (v2)

> Create up to 5,000 vehicle consumptions in one request; emissions are calculated asynchronously

[← Vehicles API](/api-reference/vehicles/overview)

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 `202 Accepted` with an
[ingest job](/api-reference/ingest-jobs/get) that calculates them. Follow it with
[`GET /v2/ingest-jobs/{id}`](/api-reference/ingest-jobs/get) or the
[`ingest_job.finished`](/api-reference/webhooks/overview) webhook.

<Warning>
  **Beta.** Part of the bulk ingest API, currently in beta. The contract may still change before general availability.
</Warning>

<RequestExample>
  ```bash cURL theme={"theme":{"light":"github-light","dark":"github-dark"}}
  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"
        }
      ]
    }'
  ```

  ```python Python theme={"theme":{"light":"github-light","dark":"github-dark"}}
  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"])
  ```
</RequestExample>

<ResponseExample>
  ```json 202 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  {
    "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"
    }
  }
  ```

  ```json 422 theme={"theme":{"light":"github-light","dark":"github-dark"}}
  {
    "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"
  }
  ```

  ```json 200 (dry_run=true) theme={"theme":{"light":"github-light","dark":"github-dark"}}
  { "dry_run": true, "submitted": 2, "valid": 2 }
  ```
</ResponseExample>

## How it works

1. **Every record is checked.** If any is invalid the **whole** request is rejected with `422` and nothing is
   written; the answer lists every invalid record, so you can fix them all at once.
2. **The consumptions are written at once**, with status `loading`. Each belongs to its vehicle's organization: the
   `x-organization-id` organization or one under it that you can write to.
3. **The job calculates them** in the background, in chunks. Each consumption ends `success` or `error`: its job item
   is then `failed` with `CALCULATION_FAILED`, e.g. when there is no emission factor for the vehicle's class, fuel and
   country in the consumption's year.
4. **When every chunk is done** the job is `completed` (or `completed_with_errors`) and `ingest_job.finished` is 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

<ParamField header="x-api-key" type="string" required>
  Your API key (see [Authentication](/api-reference/introduction#authentication)).
</ParamField>

<ParamField header="x-organization-id" type="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.
</ParamField>

<ParamField header="Idempotency-Key" type="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`.
</ParamField>

### Query Parameters

<ParamField query="dry_run" type="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.
</ParamField>

### Body Parameters

<ParamField body="records" type="array[object]" required>
  The consumptions to create, 1 to 5,000. A field not listed here is rejected (`SCHEMA_INVALID`).

  <Expandable title="record">
    <ParamField body="vehicle_id" type="uuid" required>
      The vehicle: an `id` from [Create Vehicles (v2)](/api-reference/vehicles/create-v2) or
      [List Vehicles](/api-reference/vehicles/list). It must not be archived.
    </ParamField>

    <ParamField body="quantity" type="number" required>
      How much was consumed, in `unit_id`: 0 or more, up to 3 decimals.
    </ParamField>

    <ParamField body="unit_id" type="uuid" required>
      The unit of `quantity`: one of the fuel's `fuel_units` in [List Vehicle Fuels](/api-reference/vehicle-fuels/list)
      (litres `54a709bf-79fe-4acb-ab85-5e1d7cd26eb4`, kilograms, kWh, kilometres...), or euros (see
      [A spend in euros](#a-spend-in-euros)).
    </ParamField>

    <ParamField body="start_date" type="date" required>
      First day of the period consumed, `YYYY-MM-DD`, from 1970 to next year. Its year picks the factor's year.
    </ParamField>

    <ParamField body="end_date" type="date" required>
      Last day of the period consumed, on or after `start_date`.
    </ParamField>

    <ParamField body="vehicle_fuel_id" type="uuid">
      Which of the vehicle's fuels was consumed, for a vehicle with several. Omitted: the vehicle's primary fuel.
    </ParamField>

    <ParamField body="total_spend" type="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).
    </ParamField>

    <ParamField body="currency_unit_id" type="uuid">
      The currency of `total_spend`: a unit of [`GET /v2/units`](/api-reference/units/list) whose `type` is
      `fiat_currency`, e.g. euros `d3e37f2b-0fc3-4532-82f8-3890ab56ad37`.
    </ParamField>

    <ParamField body="custom_id" type="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.
    </ParamField>

    <ParamField body="client_row_id" type="string">
      Your own reference for the record, up to 255 characters, echoed in errors and in the job's items.
    </ParamField>
  </Expandable>
</ParamField>

## Response

`202 Accepted` with the job (`entity_type: vehicle_consumptions`, `operation: create`) and a `Location` header to it.
[`GET /v2/ingest-jobs/{id}/items`](/api-reference/ingest-jobs/list-items) has one item per record, in the order sent:
its `entity_id` is the consumption's id (the one [Delete Vehicle Consumptions (v2)](/api-reference/vehicles/consumptions-delete-v2)
takes), and a `failed` item carries its `error_code`.

## Errors

[Problem details](/api-reference/errors). Per-record codes in `errors[].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`. |

Request-level errors: `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](/api-reference/rate-limits).

## Limits

| Limit | Value |
| - | - |
| Records per request | 1 to 5,000 |
| Consumptions per calculation chunk | 200 |

## Related Endpoints

<CardGroup cols={2}>
  <Card title="Delete Vehicle Consumptions (v2)" icon="trash" href="/api-reference/vehicles/consumptions-delete-v2">
    Delete consumptions by id
  </Card>

  <Card title="Create Vehicles (v2)" icon="car" href="/api-reference/vehicles/create-v2">
    The vehicles, and their ids
  </Card>

  <Card title="List Vehicle Fuels" icon="gas-pump" href="/api-reference/vehicle-fuels/list">
    Fuel and unit ids
  </Card>

  <Card title="Get Ingest Job" icon="magnifying-glass" href="/api-reference/ingest-jobs/get">
    Follow the calculation
  </Card>

  <Card title="List Job Items" icon="list" href="/api-reference/ingest-jobs/list-items">
    The consumption id and outcome of each record
  </Card>
</CardGroup>


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.