We just launched! Help us find bugs & give feedback — get Pro free.

API reference · v1

Call Profitlee from your backend.

JSON in / JSON out, Bearer-token auth. Covers Amazon FBA/FBM (US, Germany, Japan) and TikTok Shop (US, FBT + self-fulfilled) from the same endpoint — switch with the platform + mode discriminators. Available to Pro accounts only.

Quick start
  1. Subscribe to Pro.
  2. Open Account → API access and click Generate API token. Copy it immediately — it's shown only once.
  3. Send requests to /api/v1/* with the token in an Authorization header.

Authorization

Every request must include an Authorization header with your token. Tokens look like eck_live_… and are tied to a single user. Treat them like passwords — anyone with the token can read your scenarios.

Header format
Authorization: Bearer eck_live_xxxxxxxxxxxxxxxxxxxxxx

We store only a sha256 hash of the token, so we can't recover a lost one — rotate via Regenerate on the account page.

Error responses

All errors return JSON of the shape { reason: string }:

StatusreasonMeaning
401auth_requiredNo Authorization header.
401invalid_tokenToken malformed or not recognised.
403pro_requiredPlan downgraded — re-subscribe to use the API.
400invalid_jsonBody could not be parsed as JSON.
400invalid_inputBody failed schema validation. issues[] details which fields.
403scenario_limitYou're at the 100-saved-scenarios limit. Delete one before creating another. Response includes the `limit` field.
POST

/api/v1/calculate

Computes a per-unit and monthly profit breakdown. Same engine as the web calculator — no I/O, no Amazon API calls, pure math against country-accurate fee tables.

Request body

A discriminated union on mode, scoped by platform. Numbers are in the marketplace's native units: in / lb / $ for Amazon us and TikTok Shop us, cm / kg / € for Amazon de, cm / kg / ¥ for Amazon jp.

Platform + mode matrix

Each platform exposes a different set of modevalues. Sending a mode that doesn't belong to the platform returns 400 invalid_input.

FieldTypeDescription
amazon"fba" | "fbm"Fulfilled by Amazon, or self-fulfilled (FBM). Regions: us, de, jp.
tiktok_shop"fbt" | "self_fulfilled"Fulfilled by TikTok, or self-fulfilled. Regions: us only (today).

Common fields

FieldTypeDescription
platform"amazon" | "tiktok_shop" (optional)Marketplace operator. Defaults to "amazon" when omitted so older clients keep working.
region"us" | "de" | "jp"Marketplace country. TikTok Shop is US-only today; sending de/jp returns invalid_input.
mode"fba" | "fbm" | "fbt" | "self_fulfilled"Fulfilment model. Allowed values depend on platform — see the matrix above. Determines the per-mode fields below.
L, W, HnumberBox dimensions (in or cm).
weightnumberUnit weight (lb or kg).
fobnumberFOB cost per unit.
headShipnumberHead shipping cost per unit.
dutynumberDuty per unit, in listing currency. NOT a percentage — convert your tariff rate to a per-unit amount yourself (e.g. 12% × $3.50 FOB → 0.42).
pricenumberSelling price.
ppcAcosnumberPPC / ad-spend ACoS as a 0–1 fraction (e.g. 0.18 = 18%).
returnRatenumberReturn rate as 0–1.
referralPctnumberReferral / commission fee as 0–1. Used directly when referralCategory is null/omitted; otherwise auto-derived from the category + price (callers may send their last manual value).
referralCategorystring | null (optional)Slug from Profitlee's referral rate-card table (e.g. "baby_products", "watches"). Categories are platform-scoped; pass an Amazon slug with platform="amazon", a TikTok slug with platform="tiktok_shop". When set, the server resolves the effective per-unit rate from the category's tier shape and ignores referralPct.
monthlyVolumeintegerMonthly units sold (≥ 0).
isApparelbooleanWhether the item is apparel — affects Amazon JP storage rate and US apparel referral surcharges.

Amazon FBA fields (mode = "fba")

FieldTypeDescription
inboundOption"optimized" | "partial" | "single"US inbound placement option. Ignored on DE / JP (single fulfillment centre).
storageMonthsnumberHow many months the inventory sits in Amazon's FCs.
storageSeason"janSep" | "octDec"Off-peak vs peak storage rate band.

Amazon FBM fields (mode = "fbm")

FieldTypeDescription
outboundShipPerUnitnumberOutbound shipping cost per unit.
pickPackPerUnitnumberPick & pack cost per unit.
monthly3plStoragenumberTotal monthly 3PL storage (not per unit) — engine divides by monthlyVolume.

TikTok Shop FBT fields (platform = "tiktok_shop", mode = "fbt")

FieldTypeDescription
storageMonthsPastFreenumberMonths stored BEYOND TikTok's free-storage window. 0 if the inventory turns within the free period.

TikTok Shop self-fulfilled fields (platform = "tiktok_shop", mode = "self_fulfilled")

FieldTypeDescription
outboundShipPerUnitnumberOutbound shipping cost per unit.
pickPackPerUnitnumberPick & pack cost per unit.
monthly3plStoragenumberTotal monthly 3PL storage (not per unit).
Response 200

{ result: CalcOutputs }. Selected fields (all numbers rounded to 4 decimal places):

FieldTypeDescription
result.platform"amazon" | "tiktok_shop"Echoed back so multi-platform clients can route on it.
result.region"us" | "de" | "jp"Echoed back.
result.modestringEchoed back — one of the four mode values.
result.totalCostnumberPer-unit total cost (COGS + platform fees).
result.grossProfitnumberprice − totalCost.
result.netProfitAfterPPCnumberPer-unit profit after PPC and return losses.
result.netMarginAfterPPCnumberMargin as 0–1 (multiply by 100 for %).
result.monthlyNetAfterPPCnumbernetProfitAfterPPC × monthlyVolume.
result.sizeTierstringResolved size-tier name (Amazon nomenclature is reused for TikTok).
result.fulfillmentFeenumberMarketplace fulfilment fee (FBA / FBT) or shipping + pick/pack (FBM / self-fulfilled).
result.referralFeenumberReferral / commission fee per unit.
result.fixedClosingFeenumberAmazon DE Media closing fee (€0.99) when referralCategory is one of books / music_video_dvd / software / video_games_accessories; 0 otherwise. Always 0 on US / JP / TikTok.
result.storageFeenumberStorage fee per unit. JP uses an apparel-aware rate.
Examples

Amazon FBA — US (curl / Node / Python)

curl -X POST https://your-host/api/v1/calculate \
  -H "Authorization: Bearer eck_live_xxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "platform": "amazon", "region": "us", "mode": "fba", "L": 17, "W": 13, "H": 4, "weight": 1.4, "fob": 3.20, "headShip": 1.10, "duty": 0.96, "inboundOption": "single", "storageMonths": 1, "storageSeason": "janSep", "price": 24.99, "ppcAcos": 0.18, "returnRate": 0.05, "monthlyVolume": 300, "referralPct": 0.15, "isApparel": false }'

TikTok Shop FBT — US (request body only)

Same endpoint, same headers — only the body shape changes. storageMonthsPastFree replaces the Amazon storage fields; inboundOption is omitted entirely.

{
  "platform": "tiktok_shop",
  "region": "us",
  "mode": "fbt",
  "L": 8, "W": 6, "H": 2,
  "weight": 0.5,
  "fob": 2.00, "headShip": 0.40, "duty": 0.24,
  "storageMonthsPastFree": 0,
  "price": 19.99, "ppcAcos": 0.10, "returnRate": 0.05,
  "monthlyVolume": 500, "referralPct": 0.08, "isApparel": false
}

Sample response (Amazon FBA US example)

{
  "result": {
    "region": "us",
    "mode": "fba",
    "sizeTier": "Large Standard",
    "totalCost": 18.3407,
    "grossProfit": 6.6493,
    "netProfitAfterPPC": 1.7007,
    "netMarginAfterPPC": 0.0681,
    "monthlyNetAfterPPC": 510.2092,
    "fulfillmentFee": 8.3731,
    "referralFee": 3.7485,
    "storageFee": 0.399
  }
}
GET

/api/v1/scenarios

Returns the authenticated user's saved scenarios, newest-updated first. Each row includes the full inputs + outputs payload as it was computed on save (or last update).

Response 200

{ scenarios: Scenario[] }, where:

FieldTypeDescription
scenarios[].idstringStable identifier (used in the web detail URL).
scenarios[].namestringUser-assigned name.
scenarios[].platform"amazon" | "tiktok_shop"Marketplace operator (echoed from the inputs).
scenarios[].region"us" | "de" | "jp"Marketplace country.
scenarios[].mode"fba" | "fbm" | "fbt" | "self_fulfilled"Fulfilment model — value range depends on platform.
scenarios[].inputsobjectThe CalcInputs snapshot.
scenarios[].outputsobjectThe CalcOutputs computed at save (4-decimal precision).
scenarios[].createdAtstring (ISO 8601)First save.
scenarios[].updatedAtstring (ISO 8601)Most recent save/update.

No pagination yet — pages are user-bounded (typical Pro account has dozens). We'll add ?limit / ?cursor if that ever matters.

Examples
curl https://your-host/api/v1/scenarios \
  -H "Authorization: Bearer eck_live_xxxxxxxxxxxxxxxxxxxxxx"
POST

/api/v1/scenarios

Creates a new saved scenario for the authenticated user. Outputs are recomputed server-side from the supplied inputs and stored alongside them. Also appends a row to the 30-day activity feed.

Request body
FieldTypeDescription
namestringDisplay name; 1–120 chars.
inputsCalcInputsSame shape as POST /api/v1/calculate. See that section for field tables.

Response is { scenario: Scenario } (the full new row, including server-generated id). HTTP 201 on success.

Examples
curl -X POST https://your-host/api/v1/scenarios \
  -H "Authorization: Bearer eck_live_xxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Canvas tote — US Q4", "inputs": { "platform": "amazon", "region": "us", "mode": "fba", "L": 17, "W": 13, "H": 4, "weight": 1.4, "fob": 3.20, "headShip": 1.10, "duty": 0.96, "inboundOption": "single", "storageMonths": 1, "storageSeason": "janSep", "price": 24.99, "ppcAcos": 0.18, "returnRate": 0.05, "monthlyVolume": 300, "referralPct": 0.15, "isApparel": false } }'
GET

/api/v1/scenarios/{id}

Returns a single scenario by id. Scoped to the authenticated user — requesting an id you don't own returns 404 not_found.

Examples
curl https://your-host/api/v1/scenarios/SCENARIO_ID \
  -H "Authorization: Bearer eck_live_xxxxxxxxxxxxxxxxxxxxxx"
PUT

/api/v1/scenarios/{id}

Replaces the scenario's inputs and (optionally) its name. Outputs are recomputed server-side; a fresh activity-feed row is appended so iterations show up alongside saves.

Request body
FieldTypeDescription
inputsCalcInputsRequired. Replaces the stored inputs verbatim.
namestring (optional)If present (1–120 chars), the scenario is renamed.

For rename-only, prefer PATCH below — same end state, smaller payload, no history row.

Examples
curl -X PUT https://your-host/api/v1/scenarios/SCENARIO_ID \
  -H "Authorization: Bearer eck_live_xxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "inputs": { "platform": "amazon", "region": "us", "mode": "fba", "L": 17, "W": 13, "H": 4, "weight": 1.4, "fob": 3.20, "headShip": 1.10, "duty": 0.96, "inboundOption": "single", "storageMonths": 1, "storageSeason": "janSep", "price": 24.99, "ppcAcos": 0.18, "returnRate": 0.05, "monthlyVolume": 300, "referralPct": 0.15, "isApparel": false } }'
PATCH

/api/v1/scenarios/{id}

Renames a scenario. Inputs and outputs are untouched; only name and updatedAt change. Body: { name: string }.

Examples
curl -X PATCH https://your-host/api/v1/scenarios/SCENARIO_ID \
  -H "Authorization: Bearer eck_live_xxxxxxxxxxxxxxxxxxxxxx" \
  -H "Content-Type: application/json" \
  -d '{ "name": "Canvas tote — US Q4 (v2)" }'
DELETE

/api/v1/scenarios/{id}

Permanently removes the scenario and any associated share link (foreign key cascade). The 30-day activity rows it spawned are preserved — they live independently in scenario_history.

Examples
curl -X DELETE https://your-host/api/v1/scenarios/SCENARIO_ID \
  -H "Authorization: Bearer eck_live_xxxxxxxxxxxxxxxxxxxxxx"

Stability & roadmap

  • Versioning. The /v1/ namespace is stable — any breaking change ships under /v2/ with both running in parallel.
  • Rate limits.None today. We'll publish the cap ahead of turning anything on.
  • Platform coverage. Amazon FBA/FBM ships for US, Germany, Japan; TikTok Shop ships for US (FBT + self-fulfilled). DE/JP for TikTok Shop arrive when the platform officially launches those regions — the engine signature is already platform-scoped.
  • Coming next. Share-link management (create / revoke) and PDF download URLs over the API.