Skip to main content

fleet-costs-sdk

Official Python SDK for the Fleet Costs landed-cost / duty / VAT / freight pricing API.

pip install fleet-costs-sdk
from fleet import FleetClient

client = FleetClient(
    base_url="https://api.getfleet.dev",
    api_key="ck_live_...",
)

quote = client.create_quote({
    "origin": "US",
    "dest": "DE",
    "itemValue": {"amount": 100, "currency": "USD"},
    "dimsCm": {"l": 20, "w": 15, "h": 5},
    "weightKg": 0.5,
    "categoryKey": "electronics.smartphone",
    "mode": "air",
    "hs6": "851712",  # optional: supply a 6-, 8- or 10-digit commodity code
})

print(quote["total"])

Features

  • Synchronous client built on httpx.
  • Automatic retries on transient failures (429, 500, 502, 503, 504) with exponential backoff.
  • Idempotency key support on mutating endpoints — safe to retry writes.
  • Context manager support (with FleetClient(...) as client: ...).
  • Typed method surface covering quotes, classify, manifests, and lookups.

Documentation

License

MIT — see the LICENSE file included in this package.

Collection assessment

Quote methods preserve the API's assessment dictionary. Before collecting import charges, require quote["assessment"]["collection"]["eligible"] is True. Its outcome is complete, estimated, or refused; display the reasons and their message and action alongside any estimate. A maximum or confidence score is not collection permission. Category-only quotes remain estimates. Missing assessment on an older saved response requires a fresh quote with a new idempotency key before collection.

Retry and recovery keys

Mutating methods return a FleetResponse: the usual response dictionary, plus an idempotency_key attribute containing the key actually sent. The attribute is SDK metadata and is not added to the API JSON fields.

import httpx
from fleet import FleetAPIError

try:
    quote = client.create_quote(body)
    recovery_key = quote.idempotency_key
except FleetAPIError as error:
    recovery_key = error.idempotency_key
    server_code = error.code
except httpx.RequestError as error:
    recovery_key = error.idempotency_key
except ValueError as error:
    recovery_key = error.idempotency_key

For a timed-out quote, use client.get_quote_by_key(recovery_key) to retrieve a result that may have completed server-side. A missing result is not evidence that it is safe to create the same order under another key.

The SDK keeps the same key on network errors, ordinary transient HTTP failures, and 409 IDEMPOTENCY_KEY_PROCESSING. It generates a replacement only after 409 IDEMPOTENCY_KEY_FAILED explicitly confirms rollback, and only when the SDK created the key. A caller's idempotency_key is never replaced. Unrelated conflicts are not retried. Retries share the configured max_retries budget; HTTP and transport failures expose the final attempted key even when that budget is zero. Invalid JSON and HTTPX response-decoding failures also retain the key on the original exception; non-transport decoding failures are not retried.

create_quote and create_quote_batch require decoded JSON success responses to be objects. JSON null, arrays and scalars raise ValueError with the final idempotency_key, without retrying the write. This checks only the top-level shape: empty objects and nested nullable fields are retained unchanged. Invalid JSON still raises its original parsing exception with the final recovery key.

Complete consignments and batch quotes

Use client.create_quote_batch(items) for 1–50 quote inputs. All items are sent in one request, in their original order, including non-adjacent members of a declared consignment. Supply the complete membership and the canonical declaration facts; the SDK does not invent grouping, split oversized batches, or change delivery and freight inputs. An explicit zero deliveryCharge is different from an omitted value.

insurance is the premium actually paid and not already in itemValue. It is handled as freight is: part of components.CIF and total everywhere, and of the duty base only where duty is valued CIF (not the US). For a CN destination, omit it when no insurance was paid and the API applies the customs presumption of 3 per mille of goods plus freight (cn_insurance_presumed); send {"amount": 0, ...} when the insurance is already included in itemValue. In the response, insurance is the declared premium inside components.CIF and total, and presumedInsurance is the CN presumption: duty and VAT are computed on it, but it is not part of components.CIF or total, since nobody pays it. The SDK sends whichever of the three states you give it unchanged.

Inspect results, succeeded and failed: an HTTP success may contain per-item errors or estimates, and status == "ok" does not grant collection permission. Assessments and allocation details are retained unchanged.

The batch response exposes result.idempotency_key. To recover a timed-out batch, repeat create_quote_batch with the identical full items and that final key. get_quote_by_key reads single quotes and cannot replay a batch key.

Dispatch and customs status

Quote inputs are dictionaries. Single and batch methods preserve the canonical declaration.movement object without adding defaults or validating customs facts:

body = {
    "origin": "CN",  # country of manufacture
    "dispatchCountry": "DE",  # actual ship-from country
    "dest": "FR",
    "itemValue": {"amount": 200, "currency": "EUR"},
    "dimsCm": {"l": 10, "w": 10, "h": 10},
    "weightKg": 2,
    "categoryKey": "apparel.tshirt",
    "hs6": "610910",
    "mode": "air",
    "freight": {"amount": 25, "currency": "EUR"},
    "deliveryCharge": {"amount": 0, "currency": "EUR"},
    "declaration": {"movement": {"customsStatus": "free_circulation"}},
}
quote = client.create_quote(body)

The status describes the goods at this sale in the dispatch customs territory. free_circulation means qualifying goods are already in free circulation before the sale, with no release/entry lodged to fulfil this order. Use not_in_free_circulation when a customs debt or conditional procedure remains, including bonded stock that will be released to fulfil this order. This is the merchant's assertion from their records; Fleet does not verify release evidence. Do not derive it from manufacture, warehouse country, IOSS registration or an unset Boolean. Supply actual dispatchCountry with a known status; omit movement when unknown. An empty movement object or a made-up status is not an unknown value.

Movement is per line: items sharing a complete consignment may have different statuses. Preserve those facts; do not split a consignment to change its treatment. On a no-border lane, a required release produces customs_release_not_modelled and a refused assessment with a null maximum. Other qualifications remain independent, including vat_buyer_status_unresolved. Neither declared free circulation nor a successful batch item establishes collection permission, buyer VAT entitlement, seller registration or the correct remittance jurisdiction.

explainability.duty.customsStatusSource records request for a merchant declaration and assumed for an inferred status on applicable no-border quotes. It is absent on border quotes and can be absent on historical records, including records with no explainability block. Preserve that absence; never fill it with assumed or interpret request as verified. The response dictionary retains complete reasons, warnings, missing components and nullable ceilings alongside this provenance.

Distance-sales attestation

A seller established in an EU member state, with no OSS registration, can attest that it is under that state's distance-selling threshold (EUR 10,000 in the euro area, the national figure elsewhere). State the total in the threshold's currency; client.get_distance_sales_threshold("PL") says which. Quotes whose request declares dispatchCountry equal to the attested state can then price consumer sales at the dispatch state's rate:

profile = client.set_distance_sales_attestation({
    "statementVersion": 1,
    "establishmentCountry": "NL",
    "sellsOnOwnAccount": True,
    "establishedOnlyThere": True,
    "dispatchesOnlyFromThere": True,
    "noDestinationTaxationOption": True,
    "noSmallEnterpriseExemption": True,
    "priorYearWithinThreshold": True,
    "currentYearTotal": {"amount": 4200, "currency": "EUR"},
    "currentYearTotalAsOf": "2026-09-20",
    "exclusionsAcknowledged": True,
})
profile["distanceSalesAttestation"]["inEffect"]

# As soon as the current-year total passes the threshold:
client.withdraw_distance_sales_attestation()

Every statement is a literal True: send the attestation only if the seller can affirm each one. It needs a live key with self:write; Test-mode keys are refused, and the API does not check who holds the key.

explainability.vat.supplyVatSchemeSource names what decided where a consumer sale is taxed (oss_registration, destination_registration, declared_destination, attestation or assumed) on a cross-border intra-EU supply priced OSS or domestic. declared_destination means the seller states destination taxation with no OSS or destination VAT registration on file; such an OSS-priced quote cannot be collected. It is absent on reverse charge, domestic movements, imports and older saved quotes. explainability.vat.thresholdAttestation is present only where the engine consulted an attestation on file (no OSS registration, a domestic-scheme price): applied, or not_applied with a reason. Preserve both absences; do not fill them in.

Buyer VAT numbers

Send the buyer's VAT number, country prefix included, as declaration.buyer.vatNumber, and declaration.buyer.taxStatus: "vat_registered_business" only where your checkout captured it. Never derive the status from the presence of a number.

Where an EU number could decide an intra-EU supply, Fleet checks it against VIES and reports the result in explainability.vat.buyerVatCheck: status (valid, invalid, unavailable, or not_checked with a reason), numberCountry, requesterCountry, checkedAt and requestIdentifier, the VIES consultation number to keep with the invoice. The number itself is never echoed.

A check needs your VAT registration for the dispatch state recorded in Fleet as the requester. Without it the result is not_checked with reason no_requester_registration, test keys included. Live keys in production are not checked until the switch-on (#2369). Test keys get a simulator that sends nothing anywhere: national part 100 is valid and 200 invalid. GB numbers are not checked yet.

What the result changes depends on the lane, so read each quote's warnings rather than these notes:

  • A number from another member state (an exempt supply). A valid number beside vat_registered_business replaces vat_reverse_charge_conditional with the information code vat_intra_eu_exemption_number_verified, and the quote can complete. VIES confirms a registration, not who is buying, so a valid number without that status does not. VAT confidence stays capped at estimated and guaranteedMax keeps its reserve either way, because the exemption also rests on your own recapitulative statement. unavailable and not_checked leave the quote as it would be without a check.
  • A number from the dispatch state never makes the supply exempt. A valid one beside vat_registered_business prices dispatch-state VAT and discloses it with vat_reverse_charge_not_available.
  • An invalid number, from either, is priced as absent. It blocks collection with vat_buyer_vat_number_invalid only where that could change the rate; where it cannot, such as equal rates or an applied distance-sales attestation, the quote discloses it with vat_reverse_charge_not_available.

On a GB business import of GBP 135 or less, a GB number gives a conditional zero (vat_reverse_charge_conditional) with no guaranteedMax. Rows from export_quotes carry the same buyerVatCheck, so the consultation number stays linked to its quote.

For the separate organization data export, use GET /v1/data-retention/export with self:read scope as an organization owner or admin. Retained consultation records appear in data.viesConsultations, newest first, up to 10,000. completeness.viesConsultationsIncluded gives the returned count; completeness.viesConsultationsTruncated says whether more records exist. These records are separate from per-quote buyerVatCheck. This organization export excludes single-quote history, manifest quotes and manifest documents; it is not a complete quote-history export.

Great Britain and Northern Ireland

For quote and manifest requests with destination GB (or UK), explicitly send gbDeliveryTerritory: "great_britain" or "northern_ireland" for the actual delivery. No SDK supplies a default. Omission returns HTTP 422 GB_DELIVERY_TERRITORY_REQUIRED; Northern Ireland returns HTTP 422 TERRITORY_NOT_SUPPORTED. Do not derive Great Britain from the country code alone.

Gateway headers

Use the public extra_headers option when your chosen API gateway requires additional headers. For example, a Cloudflare Access protected origin can use:

import os
from fleet import FleetClient

with FleetClient(
    base_url=os.environ["FLEET_API_URL"],
    api_key=os.environ["FLEET_API_KEY"],
    extra_headers={
        "CF-Access-Client-Id": os.environ["CF_ACCESS_CLIENT_ID"],
        "CF-Access-Client-Secret": os.environ["CF_ACCESS_CLIENT_SECRET"],
    },
) as client:
    quote = client.create_quote(body)

The mapping is copied at construction. Header names are case-insensitive; invalid/duplicate names, non-ASCII/control-character values and SDK-controlled authentication, idempotency, host, framing and hop-by-hop headers raise ValueError. Keep the API key in api_key and operation keys in idempotency_key. Redirects are not followed, so gateway credentials are not forwarded to a redirect destination. Use a trusted HTTPS origin for real credentials.

Metadata

Release files for fleet-costs-sdk 0.4.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for fleet-costs-sdk 0.4.0
File Size Uploaded
fleet_costs_sdk-0.4.0.tar.gz 14.8 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for fleet-costs-sdk 0.4.0
File Interpreter ABI Platform
fleet_costs_sdk-0.4.0-py3-none-any.whl Python 3 none any Details

Total release size: 30.4 kB

Release files / fleet_costs_sdk-0.4.0.tar.gz

Download URL fleet_costs_sdk-0.4.0.tar.gz
Size 14.8 kB
Tags Source
SHA-256 checksum
How to use checksums
831f986ffdde90f836a3a3d2db1c81b6e44785c2927c00883c340086e9137579
BLAKE2b-256 checksum
How to use checksums
81a452727da3170922e7bc91d17570cb17e1c39f6bd064b49109687fb96928cd
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.

Transparency log

Release files / fleet_costs_sdk-0.4.0-py3-none-any.whl

Download URL fleet_costs_sdk-0.4.0-py3-none-any.whl
Size 15.6 kB
Tags Python 3
SHA-256 checksum
How to use checksums
0ce11d94c54639fc6d3ff9b71edb2f380884245155b4252d47345736209ab68f
BLAKE2b-256 checksum
How to use checksums
516bccd66d042d240dedbff3b0e0252961b82d530c76c2cace92b2429e392736
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Oct 10, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.4.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page