Skip to content
FreightMath

Reference

FreightMath API

One calculate call for up to 500 SKU rows. The same formulas as the site. Base URL https://freightmath.io.

Get an API key

Authentication

Send the key in the Authorization header. The value must start with the seven characters bearer , compared without case, then a token that starts with fm_.

Authorization: Bearer fm_…

A missing key, a revoked key, or any other shape returns 401 Use a bearer API key. A subscription that is not active or trialing, or that has a billing hold, returns 402 This subscription is not active.

Limits

Pro

500

API calls per calendar month. One request is one call, however many rows it contains.

Pro Plus

10,000

API calls per calendar month, plus the branded freight class embed.

  • Each JSON body accepts 1 to 500 rows. A CSV with more than 500 data rows is rejected with Send 500 rows or fewer.
  • Calculate allows 60 requests per minute for an IP address. Over that, the response is 429 Too many requests.
  • The monthly cap returns 429 Monthly API call limit reached, with limit and used.
  • Brand updates allow 30 requests an hour. Over that, the response is 429 Too many requests. Try again later.

POST /api/v1/calculate

JSON uses unitSystem. A body whose Content-Type contains text/csv uses the columns sku, unit_system, length, width, height, weight and quantity. unit_system is metric or imperial. An operator key, which is not tied to a subscription, still calculates and returns usage as null. A subscription key counts one call before the rows run.

Request

{
  "rows": [
    {
      "sku": "PAL-1",
      "unitSystem": "imperial",
      "length": 48,
      "width": 40,
      "height": 48,
      "weight": 500,
      "quantity": 1
    }
  ]
}

CSV

sku,unit_system,length,width,height,weight,quantity
PAL-1,imperial,48,40,48,500,1

Response

{
  "apiVersion": "2025-10-16.2",
  "results": [
    {
      "sku": "PAL-1",
      "ok": true,
      "freightClass": 100,
      "densityPcf": 9.375,
      "dimKg": {
        "iata": 252,
        "courier-5000": 303,
        "dhl-express": 303,
        "fedex": 303,
        "ups": 303,
        "usps": 252,
        "royal-mail": 303,
        "dpd": 303,
        "dpd-4000": 378
      },
      "dimLb": {
        "iata": 556,
        "courier-5000": 664,
        "dhl-express": 664,
        "fedex": 664,
        "ups": 664,
        "usps": 556,
        "royal-mail": 664,
        "dpd": 664,
        "dpd-4000": null
      }
    }
  ],
  "usage": {
    "used": 1,
    "limit": 500,
    "month": "2026-10"
  }
}

GET /api/pro/account

Returns the plan, calls used in the current UTC month, the period end, and, on Pro Plus, the embed snippet. An operator key returns kind admin, brandedEmbed false, and the message Operator keys are not tied to a plan.

Subscriber response

{
  "kind": "subscriber",
  "plan": "plus",
  "planName": "Pro Plus",
  "limit": 10000,
  "used": 12,
  "month": "2026-10",
  "brandedEmbed": true,
  "brand": {
    "name": "Northline",
    "color": "#0e4f5c",
    "url": null,
    "slug": "examplebrand"
  },
  "embed": "<script src=\"https://freightmath.io/embed.js\" data-brand=\"examplebrand\" async></script>",
  "cancelAtPeriodEnd": false,
  "currentPeriodEnd": "2026-11-16T00:00:00.000Z",
  "email": "ada@shipper.example"
}

POST /api/pro/account

Pro Plus only. name is 1 to 60 characters, color is a hex colour such as #0e4f5c, and url is empty or an https link of at most 200 characters. Pro receives 403 Branded embeds are included with Pro Plus. The public embed script is /embed.js with data-brand set to the subscription slug. It frames /embed/freight-class.

Request

{
  "name": "Northline",
  "color": "#0e4f5c",
  "url": "https://northline.example"
}

Response

{
  "ok": true,
  "brand": {
    "ok": true,
    "name": "Northline",
    "color": "#0e4f5c",
    "url": "https://northline.example/"
  }
}

Errors

  • 401Use a bearer API key.
  • 402This subscription is not active.
  • 403Branded embeds are included with Pro Plus.
  • 400Invalid calculation request.
  • 400Send 500 rows or fewer.
  • 400Empty CSV.
  • 400The CSV is empty.
  • 400Missing columns:
  • 400unit_system must be metric or imperial.
  • 400Enter a brand name up to 60 characters.
  • 400Use a hex colour such as #0e4f5c.
  • 400Brand link must be an https URL.
  • 400Brand link is too long.
  • 429Too many requests.
  • 429Too many requests. Try again later.
  • 429Monthly API call limit reached.
  • 503FreightMath is temporarily paused.
  • 503API keys are not available yet.

OpenAPI for these two routes is at /api/openapi.json.

Examples

curl

curl https://freightmath.io/api/v1/calculate \
  -H "Authorization: Bearer fm_your_key" \
  -H "Content-Type: application/json" \
  -d '{"rows":[{"sku":"PAL-1","unitSystem":"imperial","length":48,"width":40,"height":48,"weight":500,"quantity":1}]}'

JavaScript

const response = await fetch("https://freightmath.io/api/v1/calculate", {
  method: "POST",
  headers: {
    Authorization: "Bearer fm_your_key",
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
  "rows": [
    {
      "sku": "PAL-1",
      "unitSystem": "imperial",
      "length": 48,
      "width": 40,
      "height": 48,
      "weight": 500,
      "quantity": 1
    }
  ]
}),
});
const data = await response.json();

Python

import json
import urllib.request

body = json.dumps({"rows":[{"sku":"PAL-1","unitSystem":"imperial","length":48,"width":40,"height":48,"weight":500,"quantity":1}]}).encode()
request = urllib.request.Request(
    "https://freightmath.io/api/v1/calculate",
    data=body,
    headers={
        "Authorization": "Bearer fm_your_key",
        "Content-Type": "application/json",
    },
)
print(json.load(urllib.request.urlopen(request)))
Get an API key

Related

Analytics cookies load only if you accept. The calculators work either way. Cookie details