Documentation
Product Healer API
A REST API over your own data, for spreadsheets, dashboards and scripts. Every endpoint
reads, except POST /api/v1/extension/track, which starts tracking a product and
needs a token created with the read/write scope.
Included with the Enterprise plan (see pricing).
Authentication
Every request carries a bearer token, created at /account/api. A token is shown once when you create it; we store only a hash, so it cannot be recovered.
curl -H "Authorization: Bearer ph_YOUR_TOKEN" \
https://producthealer.tech/api/v1/tracked
Clients that cannot set an Authorization header may send X-PH-Token instead.
Rate limits
Counted per token, per minute — 60 by default, and your plan may allow more. Every reply carries the current position:
X-RateLimit-Limit: 60 X-RateLimit-Remaining: 59
Going over returns 429 with a Retry-After header in seconds.
Nothing is charged for a refused request.
Endpoints
| Method | Path | What it returns | Parameters |
|---|---|---|---|
| GET | /api/v1 |
What this token can reach, and its rate limit. | — |
| GET | /api/v1/keywords |
Your keyword bank, grouped by keyword and market. |
market optional, e.g. uklimit 1-200, default 50page default 1 |
| GET | /api/v1/hunts |
Your hunt history. |
limit 1-200page default 1 |
| GET | /api/v1/products |
Products saved in your workspace. Unlike the rest of the API, sell_price, cost_price, shipping_cost and ad_cost are MAJOR-unit decimal strings ("21.99"), in the currency of the product's market (uk GBP, us USD, de/fr/it/es/nl EUR, ae AED, sa SAR, eg EGP) - there is no currency field. A price never entered reads "0.00", not null. fee_pct is a percentage. |
market optionallimit 1-200page default 1 |
| GET | /api/v1/tracked |
Tracked products: current figures, our estimate, and the change since tracking began. |
market optionallimit 1-200 |
| GET | /api/v1/tracked/{asin} |
One tracked product with its daily history. |
days 1-730, default 90 |
| GET | /api/v1/alerts |
Alerts raised for your workspace. |
limit 1-200 |
| GET | /api/v1/suppliers |
Your supplier quotes, cheapest landed cost first. unit_cost and shipping_cost are MAJOR-unit decimal strings ("4.50") in the currency of the quote's product market (the market field; no currency field). An unset unit_cost reads "0.00"; unknown shipping is null. |
limit 1-200 |
| GET | /api/v1/rank |
Keyword rank-tracking positions. |
market optionallimit 1-200 |
| GET | /api/v1/fulfilment/stock |
UK Fulfilment: each product at our warehouse - on hand, in orders, available, damaged, on the way, and its size band (null when no size was given). Needs an active fulfilment account. | — |
| GET | /api/v1/fulfilment/orders |
UK Fulfilment: the newest 200 orders sent to the warehouse with status, delivery service, courier and tracking, whether it is express and any hold-until date. Never the buyer's name or address. | — |
| POST | /api/v1/fulfilment/orders |
UK Fulfilment: send an order from any platform to our warehouse. JSON body: {"reference": "your order number", "items": [{"sku": "MUG-1", "qty": 2}], "address": {"name", "line1", "line2", "city", "region", "postcode", "country": "GB", "phone"} (in the UAE: the emirate as "region", no postcode, a phone number), "cash_on_delivery": "optional, the amount to collect in your warehouse's currency, where it offers it", "buyer_email": "optional - when you have switched dispatch emails on, the buyer gets one email when it leaves; kept encrypted and deleted with the address", "service": "optional, one of our delivery services", "gift_note": "optional, printed on a card, no prices in the parcel", "express": "optional true - dispatched first, same day up to our express cut-off", "hold_until": "optional YYYY-MM-DD - we keep it until that day", "preorder": "optional true - wait for the stock instead of going on hold"} - or {"orders": [...]} with up to 50. Each result carries the buyer's tracking page address. Needs a read/write token. UK addresses unless we have switched international delivery on for you (then the country code, a service that goes there and customs details on every product); every SKU must be one of your fulfilment products. Sending the same reference again does nothing. The address is kept encrypted and deleted 30 days after dispatch. | — |
| GET | /api/v1/fulfilment/inbound |
UK Fulfilment: your deliveries to the warehouse and what was counted (null until counted). | — |
| GET | /api/v1/extension/lookup |
One product with fees and our estimate. Used by the browser extension. |
asin requiredmarket required |
| POST | /api/v1/extension/track |
Start tracking a product. Needs a read/write token. |
asin in the JSON bodymarket in the JSON body |
How to read the numbers
Every response separates what was observed from what we estimated, and they are never merged into one field.
observed— reported by a data provider, withobserved_atsaying when.estimate— ours, from sales rank, with aconfidenceand a note. It is an order of magnitude, not a forecast.null— we do not know. It never means zero, and you should not treat it as zero.- Keyword positions are autocomplete positions: a demand signal, not search volume. No endpoint returns a search volume, because no source gives us one.
A worked example
$ curl -s -H "Authorization: Bearer ph_…" \
https://producthealer.tech/api/v1/tracked
{
"ok": true,
"meta": { "page": 1, "limit": 50, "returned": 1 },
"tracked": [
{
"asin": "B0…",
"marketplace": "uk",
"title": "…",
"is_mine": true,
"observed": {
"price_minor": 2199, "currency": "GBP", "bsr": 2600,
"rating": 4.4, "reviews": 344, "sellers": 9,
"observed_at": "2026-09-22 06:10:04"
},
"estimate": {
"low": 120, "high": 230, "confidence": "low", "label": "120–230",
"caveat": "Estimated from sales rank. Not a measurement."
},
"since_tracking": {
"price_minor": -300, "bsr_improved_by": 1600,
"reviews": 26, "sellers": 3, "since": "2026-09-01 09:12:00"
}
}
]
}
Money here is in minor units — 2199 is £21.99 — so no rounding happens between us and you.
Two older endpoints differ: /api/v1/products and /api/v1/suppliers
return major-unit decimal strings (see their entries above).
estimate is monthly units as a low–high range, never a single number, and is
null when the sales rank is missing or outside the range our model covers.
MCP server (for AI assistants)
AI tools that speak the Model Context Protocol (such as Claude) can read your Product Healer data directly.
Add a remote MCP server with the address https://producthealer.tech/mcp and your API token as a
Bearer token (Authorization: Bearer ph_...). It uses the same token, plan check and per-minute
limit as this API, is read-only, and spends no lookups: it reads what your workspace already holds.
list_tracked_products | Products this workspace tracks, with current price, rank, sellers and the change since tracking started. |
get_product | The figures this workspace already holds for one ASIN (from Keepa, the seller's Amazon account or typed in). Spends no lookup; says when nothing is held. |
search_keyword_bank | Keywords in this workspace's keyword bank. Positions are autocomplete positions - a demand signal, not search volume. |
list_alerts | Recent alerts raised for this workspace (prices, ranks, stock, profit and more). |
search_query_performance | Amazon Search Query Performance (Brand Analytics) from the latest collected report: queries, Amazon's own search counts and this brand's share of clicks and purchases. |
amazon_search_terms | Amazon Search Terms (Brand Analytics) from the latest collected report: the most-searched terms and the products taking the clicks. |
price_list_results | Results of the latest Price List Scanner list (or a given one): each supplier line with its Amazon match, price, profit, ROI and verdict. |
A tool that is not in your plan is not offered. Transport: Streamable HTTP, POST only, answered with JSON (no event stream).
Errors
401 | No token, or the token is not recognised, revoked or expired. |
403 | The account is suspended, the token lacks the scope for that call, the
plan does not include it, or the plan's monthly allowance for it (for example product
lookups) is used up — the error says which. Retrying will not help until
the allowance resets. |
404 | No such endpoint, or you are not tracking that ASIN. |
409 | The call is valid but cannot be served yet — usually a data provider is not connected. |
429 | Per-minute rate limit for the token. Wait for Retry-After seconds. |
Every error reply is JSON with ok: false and a human-readable
error. There is no error format to special-case.