curl -i -X POST "https://api.dcycle.io/v2/logistics/requests" \
-H "x-api-key: ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7b1f9c2e-4d3a-4f8e-9a61-2c5d8e0f1a37" \
-d '{
"records": [
{
"client_row_id": "SHIP-001",
"movement_id": "SHIP-001",
"origin": "Madrid, Spain",
"destination": "Barcelona, Spain",
"origin_country": "ES",
"load": 1000,
"load_unit": "kg",
"toc": "van_3_5_t_diesel",
"shipment_date": "2026-10-01"
}
]
}'
{
"id": "4f0b8b3e-6a41-4b7e-9d0c-2c1f3a5e8d10",
"status": "processing",
"entity_type": "logistics",
"operation": "create",
"source": "api_bulk",
"counts": { "submitted": 1, "succeeded": 0, "failed": 0 },
"chunks_total": 1,
"chunks_done": 0,
"created_at": "2026-10-15T08:00:00.000Z",
"finished_at": null,
"links": {
"self": "/v2/ingest-jobs/4f0b8b3e-6a41-4b7e-9d0c-2c1f3a5e8d10",
"items": "/v2/ingest-jobs/4f0b8b3e-6a41-4b7e-9d0c-2c1f3a5e8d10/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": "SHIP-001",
"error_code": "INVALID_LOAD",
"error_params": { "detail": "Load cannot be negative or 0" }
}
],
"rejected": 1,
"request_id": "b2248a31-0f5e-4a54-9f0a-3d1f2b6c7e90"
}
{
"dry_run": true,
"submitted": 1,
"valid": 1
}
Create Logistics Requests (v2)
Create one or up to 5,000 logistics legs in one request; their distance and emissions are calculated by an ingest job
POST
/
v2
/
logistics
/
requests
curl -i -X POST "https://api.dcycle.io/v2/logistics/requests" \
-H "x-api-key: ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7b1f9c2e-4d3a-4f8e-9a61-2c5d8e0f1a37" \
-d '{
"records": [
{
"client_row_id": "SHIP-001",
"movement_id": "SHIP-001",
"origin": "Madrid, Spain",
"destination": "Barcelona, Spain",
"origin_country": "ES",
"load": 1000,
"load_unit": "kg",
"toc": "van_3_5_t_diesel",
"shipment_date": "2026-10-01"
}
]
}'
{
"id": "4f0b8b3e-6a41-4b7e-9d0c-2c1f3a5e8d10",
"status": "processing",
"entity_type": "logistics",
"operation": "create",
"source": "api_bulk",
"counts": { "submitted": 1, "succeeded": 0, "failed": 0 },
"chunks_total": 1,
"chunks_done": 0,
"created_at": "2026-10-15T08:00:00.000Z",
"finished_at": null,
"links": {
"self": "/v2/ingest-jobs/4f0b8b3e-6a41-4b7e-9d0c-2c1f3a5e8d10",
"items": "/v2/ingest-jobs/4f0b8b3e-6a41-4b7e-9d0c-2c1f3a5e8d10/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": "SHIP-001",
"error_code": "INVALID_LOAD",
"error_params": { "detail": "Load cannot be negative or 0" }
}
],
"rejected": 1,
"request_id": "b2248a31-0f5e-4a54-9f0a-3d1f2b6c7e90"
}
{
"dry_run": true,
"submitted": 1,
"valid": 1
}
← Logistics API
Creates one or up to 5,000 logistics legs (shipments) in one request. The legs are written at once and their distance and
emissions are calculated asynchronously by an ingest job: the answer is the job
(
An address the distance engine cannot geocode, or one made only of spaces or punctuation, comes as
If you send a leg again while an earlier job that contains it is still
Requests over the cap get HTTP
202, processing), never the legs.
- All or nothing. If any record is invalid the answer is
422 BULK_RECORDS_REJECTED, listing every invalid record, and nothing is written: no leg, package or hub. - Idempotent.
Idempotency-Keyis required: retrying with the same key and body returns the original job. - Per-leg results. A leg whose distance or emissions could not be calculated is a failed item of the job, with
its reason (
error_code), and stays stored without emissions.
Beta. This endpoint is in beta: the contract may still change before general availability. Pin your
integration to the fields documented here and handle unknown fields in responses gracefully. Feedback is welcome
through your Dcycle contact.
Changed on 2026-10-16. This route used to be
POST /v2/logistics/requests/bulk; only the path changed. One
route now creates one or many legs, as POST /v2/wastes and POST /v2/facilities do. See the
changelog.curl -i -X POST "https://api.dcycle.io/v2/logistics/requests" \
-H "x-api-key: ${DCYCLE_API_KEY}" \
-H "x-organization-id: ${DCYCLE_ORG_ID}" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: 7b1f9c2e-4d3a-4f8e-9a61-2c5d8e0f1a37" \
-d '{
"records": [
{
"client_row_id": "SHIP-001",
"movement_id": "SHIP-001",
"origin": "Madrid, Spain",
"destination": "Barcelona, Spain",
"origin_country": "ES",
"load": 1000,
"load_unit": "kg",
"toc": "van_3_5_t_diesel",
"shipment_date": "2026-10-01"
}
]
}'
{
"id": "4f0b8b3e-6a41-4b7e-9d0c-2c1f3a5e8d10",
"status": "processing",
"entity_type": "logistics",
"operation": "create",
"source": "api_bulk",
"counts": { "submitted": 1, "succeeded": 0, "failed": 0 },
"chunks_total": 1,
"chunks_done": 0,
"created_at": "2026-10-15T08:00:00.000Z",
"finished_at": null,
"links": {
"self": "/v2/ingest-jobs/4f0b8b3e-6a41-4b7e-9d0c-2c1f3a5e8d10",
"items": "/v2/ingest-jobs/4f0b8b3e-6a41-4b7e-9d0c-2c1f3a5e8d10/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": "SHIP-001",
"error_code": "INVALID_LOAD",
"error_params": { "detail": "Load cannot be negative or 0" }
}
],
"rejected": 1,
"request_id": "b2248a31-0f5e-4a54-9f0a-3d1f2b6c7e90"
}
{
"dry_run": true,
"submitted": 1,
"valid": 1
}
Request
Headers
string
required
Your API key. Jobs submitted with an API key notify
ingest_job.finished to the
webhook endpoints of the x-organization-id organization.string
required
UUID of the organization the legs belong to.
string
required
Must be
application/jsonstring
required
1 to 255 characters. Send a unique value per batch (a UUID works well). Retrying with the same key and body returns
the original job instead of writing the legs 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. The key is unique across
all the v2 bulk requests of your organization, not per route, and it does not expire.Query Parameters
boolean
default:"false"
Validate only: the same checks and
422, but nothing is written and no job is created; a valid batch answers 200
with {"dry_run": true, "submitted": n, "valid": n}. The Idempotency-Key is still required but not recorded, so
you can send the real request with the same key afterwards.Body Parameters
array[object]
required
The legs to create, 1 to 5,000. Fields a record does not define are ignored without an error, so a misspelled field
name (e.g.
distance instead of distance_km) is lost. How the fields are read has the
details that change the result.Show record fields
Show record fields
string
The transport operation category (vehicle type), as
toc in List TOCs,
e.g. van_3_5_t_diesel. Required unless you send category.string
road, rail, maritime or air. Required without toc: the leg then uses that category’s generic average.string
Origin address (max 500 characters). With
destination, needed when you do not send distance_km; a known
hub_id supplies it instead.string
Destination address, or the name of an active hub of your organization (replaced by its address). Max 500
characters. Needed when you do not send
distance_km.number
Distance in km, greater than 0. Used as sent. When omitted, it is computed from
origin and destination.string
default:"GLO"
ISO 3166-1 alpha-2 code of the origin country, or
GLO. Selects the region’s TOCs and emission factors.number
The load, greater than 0, in
load_unit. When omitted, the TOC’s default load is used.string
default:"kg"
kg, t/ton, pallet/pallets (1,500 kg each), teu (10,000 kg) or feu (20,000 kg).number
default:"1"
In (0, 1]; multiplies the load.
date
YYYY-MM-DD, from 1970 to next year. Its year selects the emission factors. Default: the upload date (UTC).boolean
default:"false"
Whether the vehicle was cleaned; it increases the emissions.
boolean
default:"true"
Whether a subcontractor handled the leg.
string
fis (fully integrated) or nis (non-integrated) subcontractor, as in SQAS / ISO 14083. Only with
subcontractor: true.string
Your identifier of the movement (max 255 characters). Part of the
business key: send one per leg.
string
The leg’s stretch within the movement (max 255 characters). Part of the business key.
string
The leg’s stage within the movement (max 255 characters). Part of the business key.
string
default:"default_client"
Your customer for this leg (max 255 characters). Part of the business key.
string
Your identifier of the package the leg carries (max 255 characters); legs with the same
package_key form one
package. Requires movement_id. Part of the business key.string
The name of a hub of your organization, not its id (max 255 characters). See
How the fields are read.
string
Max 255 characters.
string
Max 255 characters.
date
The date your customer placed the order. Stored for reference; not used to calculate.
string
Your key for the record (max 255 characters), echoed on its job item and in a
422.integer
Accepted but not used: the year of
shipment_date selects the emission factors.number
Accepted but recalculated as load in tonnes × distance; kept only if the leg’s calculation fails.
How the fields are read
Several mistakes are accepted with202 and calculated with a value you did not intend. Check these before you
integrate:
toc: copy thetocof List TOCs exactly, e.g.van_3_5_t_diesel. It is matched loosely (case, spaces and dots are ignored, and a TOC whose vehicle and type both appear in the text matches), so a near miss can resolve to another TOC. It is looked up in the region oforigin_country(GLOwhen omitted), then inGLO, then inEU. If it matches no TOC and you sentcategory, the leg is calculated with that category’s generic average, without an error. Sendcategoryalone only when you want that average.load_unit:kg(default),torton,palletorpallets(1,500 kg each),teu(10,000 kg) andfeu(20,000 kg), in any case. Any other value is read as kilograms, without an error.load: optional; without it, the TOC’s default load is used.load_factormultiplies the load, the default load included. The load is stored in kg with two decimals.distance_km: used as sent. When you omit it, the distance is computed fromoriginanddestinationand multiplied by a distance adjustment factor: 0.95 forroad, 0.85 formaritime, 1 forrailandair. A distance you send is not adjusted.originanddestination: a leg withoutdistance_kmneeds both (an existinghub_idsupplies the origin); a record missing either is rejected withADDRESS_REQUIRED. Adestinationequal to the name of an active hub of your organization is replaced by the hub’s address.originis not read as a hub name here; usehub_id.hub_id: a hub name, not its id. If it names an active hub of your organization, the leg’s origin is that hub’s address and theoriginyou send is ignored. Otherwise an active hub is created with this record’soriginas its address, and later records of the batch with the samehub_iduse that hub and its address. An inactive hub with that name is not reused: a new active one is created.shipment_date: optional; without it the leg is dated at the upload time (UTC).yearandtkm: not used to calculate the leg, which takes the year ofshipment_dateinstead ofyearand recalculatestkmas load in tonnes × distance. Atkmyou send stays on the leg only if its calculation fails.
Legs are upserted on their business key
The business key of a leg is, within your organization,movement_id, movement_stretch, movement_stage,
client (default_client when omitted) and package_key. A key field you omit counts as empty.
- Sending a leg again in a later batch replaces it: every field takes the value you send or its default (an
omitted
shipment_datebecomes the upload time, an omittedhub_idunlinks the hub), its emissions are cleared, and it is calculated again. Its id is kept. Changing a key field, such aspackage_key, creates a new leg and leaves the old one. - Two records of the same batch with the same key are rejected (
DUPLICATE_RECORD). - Legs created by any other route (the app, file uploads, the v1 endpoints) share the key: a leg sent here replaces the leg with the same key, wherever it came from.
Send a unique
movement_id per leg. Legs without movement_id that share client, movement_stretch and
movement_stage are the same leg: in one batch the second and later ones are rejected with DUPLICATE_RECORD, and
with them the whole batch; in a later batch each one replaces the previous one.Response
202 Accepted
The job, withLocation: /v2/ingest-jobs/{id}. A retry with the same Idempotency-Key and body answers 202 with
the same job and Location, showing its current status and counts.
{
"id": "4f0b8b3e-6a41-4b7e-9d0c-2c1f3a5e8d10",
"status": "processing",
"entity_type": "logistics",
"operation": "create",
"source": "api_bulk",
"counts": { "submitted": 1, "succeeded": 0, "failed": 0 },
"chunks_total": 1,
"chunks_done": 0,
"created_at": "2026-10-15T08:00:00.000Z",
"finished_at": null,
"links": {
"self": "/v2/ingest-jobs/4f0b8b3e-6a41-4b7e-9d0c-2c1f3a5e8d10",
"items": "/v2/ingest-jobs/4f0b8b3e-6a41-4b7e-9d0c-2c1f3a5e8d10/items"
}
}
uuid
Ingest job id. Use it with
GET /v2/ingest-jobs/{job_id}.string
processing right after the request. See Job status.string
Always
logistics for this endpoint.string
Always
create.string
api_bulk when sent with an API key; app when sent by a person signed in to Dcycle.object
submitted: legs in the request, all of them stored. succeeded: legs calculated. failed: legs whose
calculation failed; the job’s items say which ones and why.integer
Number of chunks the legs are calculated in.
integer
Chunks already calculated.
chunks_done / chunks_total is the job’s progress.datetime
When the job was created (UTC).
datetime | null
When the last chunk finished (UTC),
null while the job is processing.object
self: the job. items: its per-leg outcomes.Job status
PollGET /v2/ingest-jobs/{id} or subscribe to ingest_job.finished.
GET /v2/ingest-jobs/{id}/items maps each record (source_row_index,
client_row_id) to its leg (entity_id) and its result.
status | Meaning |
|---|---|
processing | The legs are stored; their distance and emissions are being calculated. |
completed | Every leg was calculated. |
completed_with_errors | Every chunk ran, and at least one leg (possibly all of them) failed to calculate (counts.failed > 0). |
If
chunks_done stops moving, see A job that does not finish.
Resending the batch with a new Idempotency-Key starts a second job and leaves the first one processing.Why a leg failed to calculate
A failed item’serror_code says why, and error_params.message explains it. The leg stays stored without emissions
(and without a distance, unless you sent one); the reason is only on the job item. Fix the cause and send the leg
again: with the same business key it is updated in place.
error_code | Cause | Fix |
|---|---|---|
DISTANCE_CALCULATION_ERROR | An address could not be located (or is blank), or there is no route between origin and destination | Send a more precise address, or distance_km |
SEA_CROSSING_REQUIRES_MARITIME_MODE | Origin and destination are separated by sea, so the leg cannot go by road or rail | Declare a maritime toc/category, or split the leg |
NO_DISTANCE | The route gave 0 km (e.g. between two different places, or an air or maritime leg whose two addresses are the same place) | Send distance_km; contact support if the addresses are correct |
NO_LOAD | The load converted to kilograms is below 0.005 kg, so it is stored as 0 | Send the real load |
DISTANCE_CALCULATION_ERROR; error_params.message says what failed. A road or rail leg whose two addresses are
the same place is not an error: it is calculated as 3 km (2.85 km for road, after the adjustment factor).
The leg changed while the job was running (rare)
The legs are calculated after the202, reading each leg as it is at that moment.
error_code | Cause | Fix |
|---|---|---|
NO_TOC | The leg’s TOC was retired from the catalogue before the leg was calculated | Send it again with a current toc |
NOT_FOUND | The leg was deleted before it was calculated | Send it again if you still need it |
processing, both jobs calculate it with the
data it has when their chunk runs, and both report it as succeeded. Wait for the first job to finish before you send
a leg again.
Errors
Errors follow RFC 9457 problem details (Content-Type: application/problem+json): a stable
code, a human detail, errors when there is a list of problems, and the request_id to quote to support.
422 BULK_RECORDS_REJECTED — invalid records
Nothing was written.rejected is how many records are invalid and errors lists them (up to 100) in submission
order, with source_row_index (starting at 0), your client_row_id and a stable error_code.
Each record carries one error_code: the first rule it breaks. A record with two problems shows the second one
only after you fix the first, so validate again with ?dry_run=true before sending the batch. (SCHEMA_INVALID is
the exception inside a record: its error_params.errors lists the record’s schema problems, up to 10.)
{
"type": "https://code.dcycle.io/api-reference/errors#BULK_RECORDS_REJECTED",
"title": "Unprocessable Entity",
"status": 422,
"code": "BULK_RECORDS_REJECTED",
"detail": "3 of the submitted records are invalid; nothing was written.",
"errors": [
{"source_row_index": 1, "client_row_id": "row-2", "error_code": "INVALID_LOAD",
"error_params": {"detail": "Load cannot be negative or 0"}},
{"source_row_index": 2, "client_row_id": "row-3", "error_code": "SCHEMA_INVALID",
"error_params": {"errors": [{"loc": ["category"], "type": "value_error", "msg": "Value error, Invalid category"}]}},
{"source_row_index": 3, "client_row_id": "row-4", "error_code": "DUPLICATE_RECORD",
"error_params": {"duplicate_of": 0}}
],
"rejected": 3,
"request_id": "b2248a31-0f5e-4a54-9f0a-3d1f2b6c7e90"
}
error_code | Cause | Fix |
|---|---|---|
SCHEMA_INVALID | The record does not match the schema: a wrong type; category other than road, rail, maritime or air (lowercase), or null without a toc; origin_country not ISO 3166-1 alpha-2 or GLO; load_factor outside (0, 1]; package_key without movement_id; subcontractor_type other than fis/nis, or with subcontractor: false; movement_id, movement_stretch, movement_stage, client, package_key, hub_id or a license plate over 255 characters or with a NUL character; client_row_id over 255 characters; or the record is not a JSON object. error_params.errors lists each problem (loc, type, msg) | Fix the fields listed |
INVALID_LOAD | load is 0 or negative (error_params.detail) | Send a positive load, or omit it to use the TOC’s default |
INVALID_DISTANCE | distance_km is 0 or negative (error_params.detail) | Send a positive distance_km, or omit it to have it computed |
ACTIVITY_DATE_OUT_OF_RANGE | The year of shipment_date is before 1970 or after next year (error_params.detail) | Fix the date |
ADDRESS_REQUIRED | No distance_km, and origin or destination is missing or blank (with hub_id, the hub has no address), so the distance could not be computed (error_params.fields: the missing ones) | Send both addresses, or distance_km |
ADDRESS_TOO_LONG | origin or destination (the hub’s address, when a hub replaces it) is over 500 characters (error_params: field, length, max) | Send the address alone |
TOC_NOT_FOUND | No TOC matches toc, and no generic average of category exists, in the region of origin_country, in GLO or in EU; also when neither toc nor category is sent (error_params: toc, category, region) | Use a TOC from List TOCs |
LOAD_NOT_AVAILABLE | No load, and the TOC has no default load (error_params: toc, category) | Send load |
DUPLICATE_RECORD | Another record of the batch has the same business key (error_params.duplicate_of: its index) | Send each leg once, each with its own movement_id |
422 IDEMPOTENCY_KEY_REUSED
TheIdempotency-Key was already used in this organization with a different body. Nothing is written. Send a new
key for a new batch; to retry the original batch, resend exactly the same body. The order of the fields does not
matter, but the values must be identical: 1000 and 1000.0, or null and an omitted field, are a different body.
{
"type": "https://code.dcycle.io/api-reference/errors#IDEMPOTENCY_KEY_REUSED",
"title": "Unprocessable Entity",
"status": 422,
"code": "IDEMPOTENCY_KEY_REUSED",
"detail": "This Idempotency-Key was already used with a different request body. Use a new key per batch.",
"request_id": "b2248a31-0f5e-4a54-9f0a-3d1f2b6c7e90"
}
409 IDEMPOTENCY_KEY_IN_PROGRESS
Another request with the sameIdempotency-Key was still being processed, typically your own retry after a timeout.
This request wrote no leg and created no job (packages or hubs it named may have been created; later requests reuse
them). Wait the Retry-After seconds and retry with the same key and body: you get the job of the request that went
through. See IDEMPOTENCY_KEY_IN_PROGRESS.
422 REQUEST_VALIDATION_FAILED — malformed request
The request itself is invalid: theIdempotency-Key header is missing, empty or over 255 characters; the body is not
JSON; or records is missing, empty or has more than 5,000 entries. errors lists each problem with where it is
(loc). See Errors.
401, 400, 404 and 403: key and organization
Before the records are looked at, the API key and the organization are checked. Each code has its section in Errors:401: no API key (AUTH_REQUIRED,CREDENTIALS_REQUIRED), a wrong or revoked one (INVALID_API_KEY), a key of an organization that is neither this one nor one of its parents (API_KEY_ORG_MISMATCH) or one too old to have an owner (API_KEY_NO_OWNER).400 ORGANIZATION_ID_REQUIRED: nox-organization-id;404 ORGANIZATION_NOT_FOUND: no organization has that id.403 LOGGED_USER_NOT_MEMBER: the key’s user is not a member of the organization inx-organization-id.
429 TOO_MANY_REQUESTS
More than 10 requests in flight at once with your API key on this endpoint. The request was not processed. Wait theRetry-After seconds (10, rising to 20 if you keep hitting the limit) and retry.
Examples
Create a batch and wait for the calculation
Python
import os
import time
import uuid
import requests
BASE = "https://api.dcycle.io"
HEADERS = {
"x-api-key": os.environ["DCYCLE_API_KEY"],
"x-organization-id": os.environ["DCYCLE_ORG_ID"],
}
records = [
{
"client_row_id": "SHIP-001",
"movement_id": "SHIP-001", # unique per leg: it is part of the leg's business key
"origin": "Madrid, Spain",
"destination": "Barcelona, Spain",
"origin_country": "ES",
"load": 1000,
"load_unit": "kg",
"toc": "van_3_5_t_diesel", # copied from GET /v1/logistics/tocs
"shipment_date": "2026-10-01",
}
]
# One key per batch: reuse it only to retry this same batch.
response = requests.post(
f"{BASE}/v2/logistics/requests",
headers={**HEADERS, "Idempotency-Key": str(uuid.uuid4())},
json={"records": records},
timeout=120,
)
if response.status_code == 422 and response.json().get("code") == "BULK_RECORDS_REJECTED":
for rejection in response.json()["errors"]:
print(rejection["source_row_index"], rejection["client_row_id"], rejection["error_code"],
rejection["error_params"])
raise SystemExit("Fix the listed records and send the whole batch again")
response.raise_for_status()
job_url = BASE + response.headers["Location"]
deadline = time.monotonic() + 30 * 60
while True:
job_response = requests.get(job_url, headers=HEADERS, timeout=30)
job_response.raise_for_status()
job = job_response.json()
if job["status"] != "processing":
break
if time.monotonic() > deadline:
raise SystemExit(f"Job {job['id']} is still processing after 30 minutes: contact support with its id")
time.sleep(10)
page = 1
while job["status"] == "completed_with_errors": # read every page of failures
failed = requests.get(
f"{job_url}/items",
headers=HEADERS,
params={"status": "failed", "size": 500, "page": page},
timeout=30,
).json()
for item in failed["items"]:
print("not calculated:", item["client_row_id"], item["error_code"], item["error_params"])
if page * failed["size"] >= failed["total"]:
break
page += 1
Retries and idempotency
- Network error or timeout: retry with the same
Idempotency-Keyand the same body. If the first attempt was accepted you get its job back, and nothing is written twice. 409 IDEMPOTENCY_KEY_IN_PROGRESS: the first request with that key is still being processed. WaitRetry-Afterseconds and retry with the same key and body.422 BULK_RECORDS_REJECTED: nothing was written. Fix the records and send the batch again; the same key works, since the first request created no job.422 IDEMPOTENCY_KEY_REUSED: the key already belongs to another batch. Use a new key, or resend the original body unchanged.429: too many requests in flight for your API key on this endpoint (unlike theIdempotency-Key, the limit is per key, not per organization). WaitRetry-Afterseconds; the request was not processed.
Limits
| Limit | Value |
|---|---|
| Records per request | 1 to 5,000 |
| Maximum concurrent requests | 10 per API key, per endpoint |
429 with a Retry-After header. The cap is on requests in flight at once, not on
requests per second — see Rate Limits.
Related Endpoints
Get Ingest Job
Follow the calculation
List Ingest Job Items
Per-leg outcomes, including calculation failures
List TOCs
The
toc values to sendList Requests
Retrieve requests with filtering and pagination
Was this page helpful?