REST API · v1 · OpenAPI 3.1

Company loads API

Post, update and cancel your company’s loads from your own systems, one at a time or up to 500 per call, and read them back. Available on the Elite plan in the EU, UK, US and Turkey markets.

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

MethodPathWhat 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/loads with an external_ref that already exists for your company answers 200 with { "data": <existing load>, "existing": true } instead of 201. No duplicate is created and the fields of the new body are not applied; use PATCHto change the load. This holds whatever the existing load's status, including cancelled.
  • In /bulk, such rows come back as existing with the id of the existing load and are not written again. A row that repeats an external_ref of an earlier row in the same request is an error.
  • 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.

StatusCodeMeaning
400invalid_json / validation_failedMalformed body or query parameters
401unauthorizedMissing, unknown or revoked key
402subscription_inactiveSubscription not active (payment pending, past due, cancelled, trial ended)
403plan_required / insufficient_scope / account_suspended / posting_not_allowedElite plan needed, key lacks the scope, account suspended, or not a broker in a verified company
404not_foundThe load does not exist or belongs to another company
409invalid_transition / conflict / idempotency_in_progressThe load’s status does not allow the change
413payload_too_largeBody over 2 MB or more than 500 bulk rows
422validation_failed / idempotency_key_reusedField errors are listed in "errors" (a city name that looks like a formula or junk has code "invalid_city")
429rate_limitedToo 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