Authentication and keys
Create a key at API keys in the app. Keys belong to your company: any broker of the company can list and revoke them. A key is shown once; GigFreight stores only a SHA-256 hash and the prefix (gf_xxxxxxxx) you see in the list.
Send it in the header Authorization: Bearer gf_… on every request. Keys in URLs are refused. Each key has scopes: loads:read (GET) and loads:write (POST, PATCH, DELETE, bulk). Writes need a broker account in a verified company and an active Elite plan or Elite trial.
A load you create is placed in the market of the account that created the key (EU, UK, US or Turkey), priced in that market’s currency, and marked as posted through the API. You cannot set the market, the broker, or any GigFreight classification yourself.
Endpoints
| Method | Path | What it does |
|---|---|---|
| GET | /api/v1/loads | List your company's loads Scope: loads:read. Newest first. |
| POST | /api/v1/loads | Create a load Scope: loads:write. external_ref makes creation idempotent: if a load with this external_ref already exists for your company (created by any of the company’s API keys, in any status, including cancelled), the response is 200 with { "data": <existing load>, "existing": true }. Nothing is written and the fields in the new body are not applied (use PATCH to change the load). Without external_ref every call creates a new load. Idempotency-Key, when sent, is evaluated first (see the header). |
| GET | /api/v1/loads/{id} | Get one load Scope: loads:read. Loads of other companies answer 404. |
| PATCH | /api/v1/loads/{id} | Update a load or change its status Scope: loads:write. Send content fields (draft or available loads only), or {"status": "..."} alone. Allowed status changes: draft → available | cancelled; available → draft | cancelled; interested → available | cancelled; booked → in_transit | cancelled; in_transit → delivered | cancelled. |
| DELETE | /api/v1/loads/{id} | Cancel a load (soft delete) Scope: loads:write. Sets status to cancelled; loads are never hard-deleted. Cancelling a cancelled load is a no-op; delivered loads cannot be cancelled (409). |
| POST | /api/v1/loads/bulk | Create up to 500 loads Scope: loads:write. Each row is validated like POST /api/v1/loads and gets its own result. Rows whose external_ref already exists for your company are returned as { "status": "existing", "id": <existing load id> } and not written again (their fields are not applied); a row repeating an external_ref of an earlier row in the same request is an "error". A retried bulk call is therefore safe even without Idempotency-Key; with the same Idempotency-Key and body, the first call's results are replayed as they were (rows "created" then stay "created"). Counts as one request per started block of 25 rows towards the per-key rate limit. |
| GET | /api/v1/marketplace/loads | Browse live marketplace loads Scope: loads:read. Live, real loads in the markets your account can access (EU and UK accounts: eu + uk; TR: eu + uk + tr; US: us). Expired and generated loads are never returned; no contact details. |
| GET | /api/v1/docs | This OpenAPI document |
Create a load
Required: pickup_city, pickup_date (today or later), delivery_city, delivery_date, vehicle_type. New loads are published (available) unless you send "status": "draft". Unknown fields are refused by name. City names need at least one letter (any script), may use letters, digits, spaces and . , ' ’ - ( ) / &, and must not start with = + - @; otherwise the field error has code invalid_city.
curl -X POST https://gigfreight.com/api/v1/loads \
-H "Authorization: Bearer $GIGFREIGHT_API_KEY" \
-H "Content-Type: application/json" \
-H "Idempotency-Key: order-48213-v1" \
-d '{
"external_ref": "TMS-48213",
"pickup_city": "Poznan", "origin_country": "Poland", "pickup_date": "2026-11-12",
"delivery_city": "Lyon", "destination_country": "France", "delivery_date": "2026-11-14",
"vehicle_type": "curtainsider", "weight_kg": 22000, "distance_km": 1350, "rate": 1850
}'Bulk upload and idempotency
POST /api/v1/loads/bulk takes { "loads": [ ... ] } with up to 500 loads and answers with one result per row: created, existing or error (with field errors). Valid rows are created even if others fail.
Give every load an external_ref (your own id, unique per company across all of its API keys). It makes creation idempotent:
POST /api/v1/loadswith anexternal_refthat already exists for your company answers200with{ "data": <existing load>, "existing": true }instead of201. No duplicate is created and the fields of the new body are not applied; usePATCHto change the load. This holds whatever the existing load's status, including cancelled.- In
/bulk, such rows come back asexistingwith the id of the existing load and are not written again. A row that repeats anexternal_refof an earlier row in the same request is anerror. - Without
external_ref, every call creates a new load.
The optional Idempotency-Key header (per API key) is checked first. A repeat with the same key and the same body replays the stored response exactly as it was, with the header Idempotent-Replayed: true (so a replayed create is still 201, and bulk rows that were created stay created). The same key with a different body is refused (422 idempotency_key_reused); a repeat while the first call is still running gets 409 idempotency_in_progress. Server errors (5xx) are not stored, so you can retry with the same key. A new key, or no key, with a known external_ref gets the existing answer above. Sending both is the safest retry.
HTTP/1.1 200 OK (POST /api/v1/loads, external_ref "TMS-48213" already posted)
{ "data": { "id": "6f0c…", "external_ref": "TMS-48213", "status": "available", … }, "existing": true }{
"results": [
{ "index": 0, "status": "created", "id": "6f0c…", "external_ref": "TMS-48213" },
{ "index": 1, "status": "existing", "id": "1a9e…", "external_ref": "TMS-48190" },
{ "index": 2, "status": "error", "external_ref": "TMS-48214",
"errors": [{ "field": "pickup_date", "message": "Pickup date cannot be in the past" }] }
],
"summary": { "created": 1, "existing": 1, "failed": 1 }
}Updating and cancelling
PATCH /api/v1/loads/{id} changes content fields while a load is draft or available, or the status alone ({ "status": "in_transit" }) along the normal lifecycle: draft → available → booked → in transit → delivered. Interest and booking come from carriers in the app. DELETE cancels a load; nothing is ever hard-deleted and delivered loads are final. Loads of other companies always answer 404.
Rate limits
60 requests per minute and 10,000 per day per key, and 120 per minute per client IP. A bulk call counts as one request per started block of 25 rows. Every response carries X-RateLimit-Limit, X-RateLimit-Remaining, X-RateLimit-Reset and X-Request-Id; a refused call gets 429 with Retry-After.
Errors
Every error has the same shape: { "error": { "code": "...", "message": "..." } }, plus errors with per-field messages for validation problems.
| Status | Code | Meaning |
|---|---|---|
| 400 | invalid_json / validation_failed | Malformed body or query parameters |
| 401 | unauthorized | Missing, unknown or revoked key |
| 402 | subscription_inactive | Subscription not active (payment pending, past due, cancelled, trial ended) |
| 403 | plan_required / insufficient_scope / account_suspended / posting_not_allowed | Elite plan needed, key lacks the scope, account suspended, or not a broker in a verified company |
| 404 | not_found | The load does not exist or belongs to another company |
| 409 | invalid_transition / conflict / idempotency_in_progress | The load’s status does not allow the change |
| 413 | payload_too_large | Body over 2 MB or more than 500 bulk rows |
| 422 | validation_failed / idempotency_key_reused | Field errors are listed in "errors" (a city name that looks like a formula or junk has code "invalid_city") |
| 429 | rate_limited | Too many requests; wait Retry-After seconds |
Vehicle types per market
- eu
- curtainsider, mega_trailer, box_trailer, refrigerated, swap_body, walking_floor, tanker, lowloader, flatbed, tipper, car_transporter, container, van
- tr
- curtainsider, mega_trailer, box_trailer, refrigerated, swap_body, walking_floor, tanker, lowloader, flatbed, tipper, car_transporter, container, van
- uk
- curtainsider, tautliner, mega_trailer, box_trailer, refrigerated, walking_floor, tanker, lowloader, flatbed, tipper, car_transporter, container, van
- us
- dry_van, reefer, flatbed, step_deck, lowboy, power_only, box_truck, straight_truck, cargo_van, sprinter, hotshot, gooseneck, container