OAuth2 authentication
Every request to the tastytrade API is authenticated with an OAuth2 access token sent in the Authorization header. This page explains how the flow works and shows the calls you need to make to obtain and use a token.
Base URLs
tastytrade keeps sandbox and production fully separate. Each environment has its own credentials and its own data.
| Environment | Base URL |
|---|---|
| Sandbox (certification) | https://api.cert.tastyworks.com |
| Production | https://api.tastyworks.com |
An OAuth client (and its refresh token) registered in one environment will not work in the other. Develop against sandbox first, then swap to production credentials and the production base URL.
Step 1 — Register an OAuth app
Before you can call the OAuth endpoints you must have an OAuth client registered with tastytrade, and a refresh token for that client. See tastytrade's official OAuth documentation at developer.tastytrade.com for how to register a client and obtain your credentials and refresh token. Keep your client credentials and refresh token confidential — treat them like passwords and never ship them in client-side code.
Step 2 — Exchange a refresh token for an access token
Access tokens are generated by POST /oauth/token. You exchange your refresh token for a fresh access token by sending a JSON body with grant_type set to refresh_token, your refresh_token, and your client_secret. client_id is optional — the server infers it from the refresh token, but if you do send it, it must match your client. You may also pass an optional scope (space-separated) to request a token with a subset of your grant's scopes.
curl -X POST https://api.tastyworks.com/oauth/token \
-H "User-Agent: my-custom-client/1.0" \
-H "Content-Type: application/json" \
-d '{
"grant_type": "refresh_token",
"refresh_token": "YOUR_REFRESH_TOKEN",
"client_id": "YOUR_CLIENT_ID",
"client_secret": "YOUR_CLIENT_SECRET"
}'
The response contains the access token you'll attach to subsequent requests. Refreshing does not rotate your refresh token — no new refresh token is returned, and the one you have remains valid for the life of the grant. The OAuth endpoints are not part of the service specs behind /reference; for registering a client and obtaining your initial refresh token, see tastytrade's official OAuth documentation at developer.tastytrade.com.
Access tokens are signed JWTs, valid for 15 minutes, and must be refreshed when they expire. Refreshing generates a brand-new token — you cannot extend the life of an existing one. Plan to refresh on a schedule (or lazily, just before a token's expiry) rather than per request, so you don't hammer POST /oauth/token unnecessarily.
Step 3 — Send the token on every request
The access token is a bearer token, so prefix it with Bearer in the Authorization header. You must also send a User-Agent header in product/version format on every request, or it will be rejected.
GET /accounts/{account_number}/positions HTTP/1.1
Host: api.tastyworks.com
Authorization: Bearer YOUR_ACCESS_TOKEN
User-Agent: my-custom-client/1.0
Accept: application/json
The product and version you choose don't matter as long as the format is correct — most people make it specific to their client, e.g. tastytrade-api-client/1.0. Real access tokens are much longer than the placeholder above.
What can go wrong
- 401 Unauthorized — the request had no
Authorizationheader, or the access token is missing, invalid, or expired. Refresh the token and retry. - 403 Forbidden — the token is valid but not authorized for that resource (for example, an account belonging to a different customer, or a token whose OAuth scope doesn't cover the operation — reads need the
readscope, mutations needtrade). - 429 Too Many Requests — you've exceeded reasonable request thresholds. Back off and retry. See /docs/guides/rate-limits-and-backoff.
Error responses include an error object with code and message. The full list of HTTP status codes (400/401/403/404/422/429/5xx) lives at /reference/errors.
Notes for money-moving requests
When you place orders, always run the order as a dry-run first to validate it, then submit with a unique external-identifier in the request body so you can recognize the order later. There is no idempotency-key header, and the API does not deduplicate retried submissions. If the outcome of a submission is uncertain (timeout or 5xx), check GET /accounts/{account_number}/orders (or /accounts/{account_number}/orders/live) for your external-identifier before resubmitting. See /docs/guides/idempotency-and-retries.
Next steps
- /docs/get-started — your first authenticated call
- /reference — full endpoint definitions for the API services
- /docs/sdks-and-tools/mcp-server — let the MCP server manage tokens for you