Torn Intel API

Torn Intel exposes a small read-only API alongside the faction dashboard. Most of the endpoints below need no key, no header and no cookie — they answer an anonymous request. The three abroad-stock feeds require an approved client key, free but issued by hand. Everything else under /api is scoped to a signed-in player.

The machine-readable version of this page is an OpenAPI 3.1 document, generated from the same source, so the two cannot disagree.

Authentication

Most endpoints on this page take no credentials. The three listed under Approved-client endpoints need a client key, sent as X-Torn-Intel-Key or as Authorization: Bearer <key>. A key identifies an application rather than a player: it unlocks no faction data and carries its own rate limit.

Those three answered anonymous requests until 2026-08-30. If a client of yours stopped working on that date, this is why — the response shapes did not change, only who may call them. Keys are free and issued manually: write via the contact page, saying which endpoints you need and roughly how often you intend to call them. A refusal is 401 (no key, or a client not yet approved) or 403 (access withdrawn); neither is transient, so retrying will not change the outcome.

Browser extensions are the documented exception: they cannot hold a secret, so a request carrying browser fetch-metadata headers reaches /api/v1/foreign-stock/travel-table without a key, under a per-IP quota. Server-side clients never send those headers and so never enter that lane.

Every other endpoint authenticates with the torn-intel-session cookie, a JWT issued by POST /api/auth/login in exchange for a Torn API key, and returns the signed-in player's own faction data only. There is no endpoint that reads an arbitrary faction and no parameter that makes one.

How Torn API keys are encrypted at rest, and which Torn permission level unlocks what, is documented on the Security page.

Torn Intel MCP server

A remote Model Context Protocol server runs at /api/mcp over the Streamable HTTP transport (spec revision 2025-06-18). It is read-only and exposes exactly the surface below, under the same access levels — an MCP server that could reach further than the REST API, or reach it more cheaply, would be a way around the boundary rather than a convenience. The two abroad-stock tools therefore require the same client key and answer with an error result without one.

claude mcp add --transport http torn-intel https://torn-intel.com/api/mcp

Tools:

Versioning & deprecation

Rate limits

Three ceilings, depending on how you reach an endpoint. The session-scoped API is not metered by this policy.

LanePer 60sCounted per
Open endpoints120client
Approved key, gated endpoint1key — default, raised on request
Browser extension, gated endpoint30IP address

Errors

Every error under /api is JSON, never an HTML page, and carries a stable machine-readable code plus a resolution saying what to do about it. Branch on code, not on the prose fields.

{
  "error": "Not Found",
  "code": "not_found",
  "message": "No API endpoint exists at /api/nope.",
  "resolution": "Check the endpoint list at https://torn-intel.com/developers ...",
  "status": 404,
  "documentation_url": "https://torn-intel.com/developers"
}

Rules of use

Open endpoints

These answer any anonymous request — no key, no header, no cookie.

GET /api/v1/public/stats

How many Torn players are indexed, how many factions are watched, how many have registered on the site, and how many factions the global scanner walks on every 20-minute pass.

Response
application/json
Cache
revalidated every 300s
Access
none
{ "playersIndexed": 817418, "watchedFactions": 240, "factionsRegistered": 1728, "factionsScanned": 11362 }

GET /api/v1/public/forum-stats

Registered factions, Pro factions, watched factions and spied players, as four integers.

Response
application/json
Cache
public, max-age=3600, s-maxage=3600
Access
none
{ "factions": 318, "proFactions": 74, "watchedFactions": 1462, "spiedPlayers": 88140 }

GET /api/v1/public/registered

Returns a single boolean keyed by Torn player ID. Exposes no personal data, which is why it is unauthenticated and open to cross-origin calls from the in-game script.

Response
application/json
Cache
not cached
Access
none
CORS
Allowed from any origin
  • playerId (integer, required) — Torn player ID (XID).
  • 400 — playerId missing or not a number.
{ "registered": true }

GET /api/v1/news/public

Newest first. Each article carries id, title, content, tag, optional image URL and publish date.

Response
application/json
Cache
not cached
Access
none
{
  "articles": [
    {
      "id": 12,
      "title": "Departure planner rewrite",
      "content": "…",
      "tag": "feature",
      "image_url": null,
      "published_at": "2026-08-24T18:02:00.000Z"
    }
  ]
}

GET /api/v1/stats-badge

A 440×56 SVG showing registered factions, watched factions and spied players. Safe to hotlink.

Response
image/svg+xml
Cache
not cached
Access
none

GET /api/v1/intel-badge

A 380×56 SVG showing how many factions and how many players the index covers.

Response
image/svg+xml
Cache
not cached
Access
none

GET /api/v1/public/pro-badge

A 300×44 PNG (drawn at 2x) reading "N Pro factions trust Torn Intel", in the home page's fonts and colours.

Response
image/png
Cache
public, max-age=300, s-maxage=300, stale-while-revalidate=600
Access
none

Approved-client endpoints

These require an approved client key as of 2026-08-30, sent as X-Torn-Intel-Key. They answered anonymous requests before that date and their response shapes are unchanged — only who may call them. Ask for a key on the contact page.

GET /api/v1/public/foreign-stock

Current foreign stock, grouped by country. Each item carries quantity, buy cost, Torn market value and the resulting profit per item. Restock predictions are not exposed here: they are available to signed-in users only. Requires an approved client key.

Response
application/json
Cache
private, max-age=20
Access
approved client key
{
  "generatedAt": "2026-08-25T09:41:02.113Z",
  "source": "torntools",
  "countries": [
    {
      "country": "mex",
      "updatedAt": "2026-08-25T09:40:47.000Z",
      "items": [
        {
          "itemId": 206,
          "itemName": "Xanax",
          "category": "Drug",
          "quantity": 45,
          "cost": 250000,
          "marketValue": 820000,
          "profitPerItem": 570000
        }
      ]
    }
  ]
}

GET /api/v1/public/foreign-stock/history

Quantity and price as observed over a rolling window, oldest point first. Observations only — no forecast. Requires an approved client key.

Response
application/json
Cache
private, max-age=30
Access
approved client key
  • itemId (integer, required) — Torn item ID.
  • country (string, required) — Destination code as returned by the stock endpoint.
  • hours (integer, optional) — Window length in hours. Clamped to 1–48. Defaults to 24.
  • 400 — itemId or country missing, or itemId not a number. Authentication is evaluated before parameters, so a call with no approved key answers 401 whatever the query string says.
{
  "country": "mex",
  "itemId": 206,
  "hours": 24,
  "points": [
    { "t": "2026-08-24T10:00:11.000Z", "quantity": 120, "cost": 250000, "marketValue": 818000 }
  ]
}

GET /api/v1/foreign-stock/travel-table

Stock per country in the shape the Torn travel-table ecosystem expects: `stocks` keyed by destination code, each with an `update` unix timestamp and a `stocks` array of id/name/quantity/cost. Torn Intel is a first-priority provider for the TornTools Travel Table, which is why this shape is fixed and will not change. Browser extensions reach it without a key; server-side clients need one.

Response
application/json
Cache
private, max-age=10
Access
approved client key, or a request issued by a browser extension
{
  "stocks": {
    "mex": {
      "update": 1756543247,
      "stocks": [
        { "id": 206, "name": "Xanax", "quantity": 45, "cost": 250000 }
      ]
    }
  },
  "timestamp": 1756543260
}

Reading pages as Markdown

Every public page on Torn Intel serves a Markdown representation from its own URL, so an agent can skip the layout markup. Ask for it with an Accept header, or append .md to the path:

curl -H "Accept: text/markdown" https://torn-intel.com/abroad-stock
curl https://torn-intel.com/abroad-stock.md

Responses carry Vary: Accept, so a cache in between will not hand you the wrong variant. A request whose Accept rules out both text/html and text/markdown gets a 406 listing the available types rather than a silent fallback.