Get started
Authentication
Two ways in: sign in with your browser from an agent, or put a key in your code.
Two ways in, one account: sign in with your browser, or put a key in your code. Both end up in the same header and count against the same limits.
By browser
For anything that can open a browser window: Claude, ChatGPT, Claude Code, Codex and Cursor. There is no key to copy. The first time the host calls TradingPal it gets a 401 with a pointer, opens your browser on a TradingPal page, you sign in and approve, and the host keeps a token it refreshes by itself.
Where you sign in
- ChatGPT and Claude: when you create the plugin or connect the connector (Quickstart, step 3).
- Claude Code: type
/mcp, pick tradingpal and choose sign in. - Codex: run
codex mcp login tradingpal. - Cursor: click Needs login next to the server.
Where it shows up
The approval appears on API Keys as a connected app, named after the host. Revoking it signs that host out; otherwise its tokens expire on their own, seven days for access and ninety for refresh. The MCP page has the flow step by step for anyone writing a host.
By code
For a script, a notebook or a server, anything that cannot open a browser: a key.
Create a key
Create one on API Keys, one per thing that will use it. The full key is shown once, at creation; after that the page keeps its prefix, name, creation time and last use. Put it in a .env file your code reads, kept out of version control:
TRADINGPAL_API_KEY=tp_live_…Send it in the header
Authorization: Bearer tp_live_…Keys start with tp_live_; the tokens a browser sign-in produces start with tp_oat_. The REST API and the MCP server accept either in the same header.
set -a; source .env; set +a
curl "https://api.tradingpal.io/api/v1/me" \
-H "Authorization: Bearer $TRADINGPAL_API_KEY"Key lifecycle
- Shown once. The server stores a hash; the full key never appears again.
- Revoke any time. Revoking is immediate; anything still using the key gets
invalid_api_key. - Rotate by creating, switching, revoking. Create the new key, move it into place, then revoke the old one. Nothing is lost in between.
- Keep it out of transcripts. An agent host reads a key from its configuration or the environment, never from the conversation. If you paste a prompt that includes a key, treat that conversation as private and rotate the key when you are done.
Either way
The plan
API and MCP access requires Premium+ subscription at $30/month; it includes all of the benefits in Premium plus API and MCP access, and Premium alone does not unlock them. A sign-in or a key on an account without the plan gets forbidden until the account has it; the key itself stays valid.
Which routes need it
| Routes | Sign-in or key |
|---|---|
/me, /scan, /tickers, /tickers/{symbol}/patterns, /tickers/{symbol}/history, /tickers/{symbol}/chart.png | Required. These read the scan and the setups on any ticker, or read or change the account's tracked tickers. |
/families, /families/{family}/stats, /universe | Optional. Keyless calls are limited per IP; keyed calls count against the key. |
/demo, /demo/{symbol}, /demo/{symbol}/chart.png | None. A fixed set of large caps, so an agent can show the data before anyone signs up. |
| MCP server | Signs the user in, or takes the key header. https://api.tradingpal.io/mcp/demo needs neither: demo and get_family_track_record work there, the other tools return an error that says how to connect. |
Name your client
Optionally send X-TradingPal-Client: <your agent or script name>. Every request is logged with it, so when you ask about usage we can tell your script's calls from your agent's. MCP hosts are recorded by the client name they send at initialize.
Errors
| Status | Code | When |
|---|---|---|
| 401 | missing_api_key | The header was not sent on a route that needs it. |
| 401 | invalid_api_key | Unknown or revoked key. |
| 401 | invalid_token | A sign-in token that expired or was revoked. The host refreshes it or signs in again; the WWW-Authenticate header says which. |
| 403 | forbidden | The account is not on Premium+. |
| 429 | rate_limited | Over the per-key or per-IP limit; details.retry_after_seconds says how long to wait. |
Every error body has the same shape, and its fix field says what to do next.