The Partner API lets you order vehicle history reports by VIN and download them as PDF. Reports are generated asynchronously: you create an order, then either poll its status or receive a webhook when it is ready.
Billing is prepaid: every accepted order is charged against your account balance, and failed orders are refunded automatically.
https://api.notedcar.com/partner/v1
Authentication
Every request carries your API key as a bearer token. Keys are created in your dashboard and shown once. You may hold several active keys and rotate them without downtime; a key can be restricted to a list of source IP ranges. Keep keys server-side; never embed them in client applications.
Keys come in two modes. nc_live_… keys order real reports and spend balance. nc_test_… keys are sandbox keys: same account, same endpoints, deterministic answers, fixture PDFs, nothing charged and no data source called (see Sandbox below).
JSON in, JSON out (Content-Type: application/json). Timestamps are ISO 8601 in UTC. Money is in integer minor units of your account currency: kopiyky for UAH (the default), cents for USD; field names use _cents for both.
VINs are 17 characters; the letters I, O and Q are not used. Lowercase is accepted and normalised. language is en (English) or uk (Ukrainian). Ids carry a product prefix: history reports hr_…, top-ups top_…, ledger entries led_…, webhook events evt_….
Errors
Errors share one envelope with a stable machine-readable code; details names the field or state when there is one.
{
"error": {
"code": "insufficient_balance",
"message": "Account balance is too low for this order.",
"details": { "required_cents": 4500, "balance_cents": 1200 }
}
}
HTTP
code
Meaning
400
invalid_request
Malformed body, bad VIN or language, or an Idempotency-Key reused with a different body
401
unauthorized
Missing, invalid, expired or revoked key, suspended account, or IP outside the key’s allowlist
402
insufficient_balance
Balance plus credit limit cannot cover the order
404
not_found
Unknown id
422
report_unavailable
No history report exists for this VIN; nothing charged
429
rate_limited
Availability checks or top-up creation; see Retry-After
503
provider_unavailable
Upstream data source or payment provider temporarily unavailable; retry later
Rate limits
Orders, status polling and billing endpoints are not rate-limited. Two calls are: GET /history-reports/availability (120 per minute per key, because a never-seen VIN reaches the upstream data source) and POST /topups (10 per minute per account, because each opens a payment at the bank or reserves an on-chain amount).
Availability responses carry X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset (Unix seconds); a 429 includes Retry-After in seconds.
Idempotency
Send an Idempotency-Key header (any unique string up to 128 characters) with POST /history-reports. If the same key is sent again within 24 hours with the same body, you receive the original response and are not charged twice. The same key with a different body returns 400 invalid_request.
How orders are priced
You pay for a VIN once. The first order for a VIN costs report_cents of your current tier; every later order for the same VIN is free, in any language, as long as the first one succeeded (failed orders are refunded and do not count).
A report is a CARFAX snapshot dated by data_as_of, the “information available as of …” date printed on it. The data source keeps one snapshot per VIN and does not refresh it, so data_as_of can be earlier than the day you order, and ordering the VIN again returns the same data.
Tiers are marginal and reset monthly (UTC): an order’s price is the tier that month_to_date_volume + 1 falls into, counting paid orders only. Every response that creates or shows an order includes price_cents.
Account
GET/account
Your account, balance, price list and limits
pricing and limits are keyed by product, so later products add a key rather than change the existing ones. mode is the mode of the key you called with.
401Missing, invalid, expired or revoked key, suspended account, or IP outside the key’s allowlist (unauthorized).
History reports
GET/history-reports/availabilityRate-limited: 120 requests per minute per key.
Free check whether a report can be produced for a VIN
Call this before ordering if you want to avoid 422 report_unavailable; it is not required. already_ordered is true when you have a ready report for this VIN; data_as_of is then its CARFAX data date and price_cents is 0.
200OK (available: false when the data source has no report)
400invalid_request — malformed VIN
401Missing, invalid, expired or revoked key, suspended account, or IP outside the key’s allowlist (unauthorized).
429rate_limited
503provider_unavailable
POST/history-reports
Order a report
The charge is made when the order is accepted. If generation fails, the order moves to failed and the charge is refunded within the same operation.
201 Created with status: "ready" when the report can be delivered immediately (download_url is set). 202 Accepted with status: "queued" otherwise: poll GET /history-reports/{id} or wait for the webhook. Typical generation time is 20–60 seconds.
Headers
Idempotency-Key
string ≤ 128
Optional. Same key + same body within 24 h replays the original response.
401Missing, invalid, expired or revoked key, suspended account, or IP outside the key’s allowlist (unauthorized).
402insufficient_balance
422report_unavailable — nothing charged
503provider_unavailable
POST/history-reports/batch
Order up to 100 reports in one call
Each item is accepted or refused on its own; the response is always 200 and lists one result per item in the same order, with the HTTP status that item would have received alone.
Differences from single orders: the availability pre-check is skipped so the call stays fast, which means a VIN with no report is accepted, then fails with vin_not_found and is refunded; vehicle is null on accepted items until the report is ready; a duplicate vin + language inside one batch is refused; Idempotency-Key is not supported.
Body
items*
array (1–100)
Objects with vin (required), language, client_reference
400invalid_request — items missing, empty or over 100
401Missing, invalid, expired or revoked key, suspended account, or IP outside the key’s allowlist (unauthorized).
GET/history-reports/{id}
Order status and download link
When status is ready, data_as_of is the date printed on the report (“information available as of …”, when CARFAX data for this VIN was pulled) and download_url is a signed link to the PDF valid for one hour; call this endpoint again for a fresh link. Report files are retained, so a link can always be regenerated.
Status values: queued, processing, ready, failed. Failure codes: vin_not_found, render_failed, provider_error. All of them are refunded.
401Missing, invalid, expired or revoked key, suspended account, or IP outside the key’s allowlist (unauthorized).
Top-ups
POST/topupsRate-limited: 10 requests per minute per account.
Add funds to your balance
Returns the payment to complete; the balance is credited once the provider confirms, usually within seconds for card and within a minute or two on-chain. A topup.paid webhook is sent when it lands.
For crypto_direct there is no payment_url; crypto_payment holds what to send and where: { network, network_name, currency, amount, address, qr_svg, expires_at, expired }. Send exactly amount (the trailing decimals identify your order) before expires_at, 30 minutes after creation.
Statuses: pending → processing (payment seen, crediting) → paid; or payment_failed (declined, expired or cancelled; nothing credited).
Body
amount_cents*
integer
In your account currency; bounds in GET /account → topups
payment_method
monobank | crypto_direct
Default monobank (card). Only methods listed in GET /account → topups.methods are accepted
payment_network
string
crypto_direct only, one of topups.crypto_networks[].id
payment_currency
string
crypto_direct only, one of that network’s currencies
return_url
https URL
Optional, where the card payment page sends the payer afterwards
401Missing, invalid, expired or revoked key, suspended account, or IP outside the key’s allowlist (unauthorized).
404not_found
GET/topups
Your top-ups, newest first
Same cursor pagination as /history-reports.
Query parameters
limit
integer
Default 50, max 200
cursor
string
From next_cursor
Responses
200OK
401Missing, invalid, expired or revoked key, suspended account, or IP outside the key’s allowlist (unauthorized).
POST/topups/{id}/claim
Claim an on-chain payment by transaction hash
For a crypto_direct top-up you paid after it stopped being watched, or when you do not want to wait for the next scan. Answers 200 with state: "paid", or a waiting state (not_found, unconfirmed, unavailable) to poll through.
401Missing, invalid, expired or revoked key, suspended account, or IP outside the key’s allowlist (unauthorized).
404not_found
422invalid_request with details.state saying why the transaction does not pay for this top-up
Billing
GET/ledger
Balance history, newest first
Same cursor pagination as /history-reports. kind is one of topup, charge, refund, adjustment; charges are negative; each entry names the product it belongs to.
400invalid_request — bad dates or a range over 92 days
401Missing, invalid, expired or revoked key, suspended account, or IP outside the key’s allowlist (unauthorized).
Webhooks
If a webhook URL is configured in your dashboard, we POST an event when an order finishes or a top-up lands. Event types are <product>.<event>: history_report.ready, history_report.failed, account.low_balance, topup.paid (data is the top-up object).
The signature is HMAC-SHA256 of "{timestamp}.{raw_body}" using your webhook secret, hex encoded, sent as X-NotedCar-Signature: sha256=… with X-NotedCar-Timestamp. Reject requests whose timestamp is more than five minutes old. Respond with any 2xx within 10 seconds; we retry up to 8 times over about six hours with increasing delays. Deliveries are at-least-once, so handle duplicates by event id.
Use an nc_test_… key to integrate without spending anything. No balance is charged and no data source is called. Orders carry "sandbox": true and are listed separately from live orders (a live key never sees them and vice versa); GET /usage and GET /ledger ignore them. Ready sandbox orders download a fixed sample report in the requested language; price_cents shows what the order would cost live. Webhooks are delivered and signed exactly as for live orders. Top-ups are not sandboxed.
The answer is decided by the last four characters of the VIN:
VIN ends with
Result
0000
422 report_unavailable
1111
202 queued, then ready about five seconds later (webhook sent)
9999
202 queued, then failed with vin_not_found about five seconds later (webhook sent)
anything else
201 ready immediately
Quick start
Three calls from a terminal: check a VIN, order, poll until ready.
# 1. Check availability (free)
curl -s "https://api.notedcar.com/partner/v1/history-reports/availability?vin=1HGCM82633A004352" \
-H "Authorization: Bearer $NOTEDCAR_KEY"
# 2. Order
curl -s -X POST https://api.notedcar.com/partner/v1/history-reports \
-H "Authorization: Bearer $NOTEDCAR_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-88213" \
-d '{"vin":"1HGCM82633A004352","language":"en","client_reference":"order-88213"}'
# 3. Poll until status is ready, then download
curl -s https://api.notedcar.com/partner/v1/history-reports/hr_01J9R5X3M6Q7V8W9Y0Z1A2B3C4 \
-H "Authorization: Bearer $NOTEDCAR_KEY"