A read-only history of your own Path of Exile 2 sales. Reachable at
https://api.igrorik.com, or on the tailnet if you are on it.
Every sale the bot sees while polling your trade history is stored here before any notification goes out. The feed is a projection of that polling and never makes a request of its own to Path of Exile, so a sale appears here at poll cadence — roughly every 20 minutes — and not sooner.
A bearer key, issued out of band and bound to one account. You only ever see your own sales; the query is scoped to your id server-side.
curl -H "Authorization: Bearer <key>" \
"https://api.igrorik.com/v1/sales?limit=100"
Missing, malformed, revoked or wrong-scope keys all answer 401 alike.
| Method | Path | What |
|---|---|---|
| GET | /v1/health | {"ok": true}. No key needed. |
| GET | /v1/sales | Your sales, oldest-first, cursor-paged. |
| GET | /v1/docs | This page. |
Nothing else exists, and nothing here accepts anything but GET.
| Query | Default | Meaning |
|---|---|---|
after | the start of retained history | An opaque cursor from a previous response. Returns events strictly after it. |
limit | 100 |
1–500. Anything else is 400. |
{
"events": [
{
"event_id": "poe2_9f1c…",
"game": "poe2",
"league": "Rise of the Abyssal",
"item": {
"name": "Mageblood",
"base_type": "Heavy Belt",
"text": null,
"id": "5d1a…",
"json": { "baseType": "Heavy Belt", "ilvl": 84, "explicitMods": ["…"], "…": "…" }
},
"price": { "amount": 120, "currency": "divine" },
"sold_at": "2026-09-10T12:02:00Z",
"detected_at": "2026-09-10T12:23:48Z",
"detection_method": "trade_history"
}
],
"next_cursor": "djE6MTQyMA",
"has_more": false,
"retention": {
"oldest_available_cursor": "djE6MA",
"retained_since": "2026-09-15T21:18:33Z",
"policy": "unlimited"
}
}
| Field | Meaning |
|---|---|
event_id |
Stable identity, derived from source fields only — never from when we noticed it or how we classified it. Use it to deduplicate. The same item sold twice is two events. |
item.json |
The trade API's own item object, verbatim: baseType, rarity,
ilvl, implicitMods, explicitMods,
runeMods, properties, requirements,
sockets, icon, corrupted. Everything about the
item lives here; item.name and item.base_type are lifted out
for convenience. |
item.text |
Always null. PoE's history endpoint carries no Ctrl-C item text. |
price |
As listed, never converted. amount is a number or null,
currency is the source string (divine, exalted, …). |
sold_at |
The sale's own time from the source, ISO-8601 UTC, or null when it could
not be parsed. Never a substitute for detected_at. |
detected_at |
When the poll that found it ran. Every event from one poll shares it. |
detection_method |
trade_history — the record was new when first seen.
trade_history_backfill — it was already in the history window at that
point. A first sync is mostly backfill: alert on trade_history only,
or you will announce a year of sales at once. Both values mean a confirmed sale from
PoE's own endpoint; nothing here is inferred from a listing disappearing. |
item.name (or item.base_type) and price — the feed
carries data, not someone else's formatting.
Cursors are opaque strings over an internal sequence, oldest-first. Keep the
next_cursor from your last successful read and pass it as after next
time; when has_more is true, keep going immediately instead of waiting.
A cursor never expires on its own — only retention can outrun it.
Every error has the same shape: {"error": {"code": …, "message": …}}, sometimes
with extra keys.
| Status | Code | What to do |
|---|---|---|
| 400 | invalid_limit, invalid_cursor |
Fix the request; a malformed cursor is never retried into working. |
| 401 | unauthorized | The key is missing, wrong or revoked. |
| 410 | cursor_expired |
Your cursor predates retained history. The body carries
oldest_available_cursor — resync from it rather than skipping silently. |
| 429 | rate_limited |
60 requests per minute per key. Retry-After and
retry_after_seconds say how long. |
| 500 | internal | Retry later; nothing was lost. |
| When | What |
|---|---|
| 2026-09-18 | notification_text removed. It carried the exact Telegram message,
markdown and emoji included, which meant nothing to a client that is not Telegram.
Compose your own line from item and price. |
| 2026-09-18 | detection_method means "was it new", not "was a message sent".
Same two values; trade_history now appears on every sale newer than the
last one seen, including ones the bot summarised rather than announced. |