Skip to content
Console

Error codes

This page lists errors you can hit when calling the ExpTech API. The body depends on the source: auth and quota failures have no body, OAuth2 returns JSON, and an SSE stream returns plain text or stream events. Read the status code first, then decide whether to parse the body.

Status Source Condition Body What to do
302 Data API Some APIs redirect a query to its canonical form (see each endpoint) Redirect Follow the redirect (curl -L; fetch follows by default)
304 Data API If-None-Match was sent and the data is unchanged Empty Keep the cached copy
400 Data API Bad parameters (each endpoint says whether it returns 400) Depends on the endpoint Fix the parameters
400 SSE topics is missing, or the topic name does not exist Plain text Fix the topic name
400 OAuth2 See OAuth2 errors JSON Fix the request
401 HTTP APIs that require a token No X-Api-Token, or the token is invalid or revoked Empty Check the header name and the token
401 SSE The token is invalid or revoked (invalid token); a topic that requires a permission was requested with no token (unauthorized) Plain text Check the token
401 OAuth2 Client authentication failed (JSON invalid_client); the userinfo token is invalid (empty body) JSON or empty Check the client settings or the token
403 HTTP APIs that require a token The token lacks that permission; the account is not subscribed to that resource; the server cannot look up quota data right now Empty Check permissions and the subscription
403 SSE forbidden: token scope missing, forbidden: not subscribed Plain text Check permissions and the subscription
403 OAuth2 too_many_tokens: the user already has 25 tokens JSON Ask the user to delete tokens they do not use
404 All The path does not exist; the data does not exist (see each endpoint); the node you named does not serve this route (see Servers and Base URL) Empty or an HTML page Check the path and the host
405 SSE, permission list A method other than GET was used SSE: method not allowed; permission list: empty Use GET
429 HTTP APIs that require a token Today’s quota is used up Empty Retry after 00:00 Taipei time, or raise the plan
429 SSE A topic that requires a permission is over quota (quota exceeded) Plain text Same as above; a reconnect within about 60 seconds is still rejected
429 OAuth2 The same IP called OAuth2 endpoints too often (about 4 times per second, with a short burst of 5) HTML page Slow down and retry
500 OAuth2, data API Internal server error JSON, or whatever that endpoint returns Retry later
502, 503, 504 All The server or an upstream is temporarily unavailable May be an HTML page Switch host or retry later. See Servers and Base URL
503 SSE The auth service is temporarily unreachable (auth service unavailable), and this request included a topic that requires a permission Plain text Reconnect later

When authentication fails on an HTTP API that requires a token, the response is only a status code. There is no JSON and no WWW-Authenticate header. The conditions for each status are in Usage and quota.

Data APIs (earthquakes, weather, map tiles, and so on) use an error format that depends on the endpoint. It is written in each page’s responses and Notes. In common:

  • Many APIs do not return 400 for an invalid parameter. They ignore it or fall back to the default. Follow the page for that API.
  • A 404 or 5xx can be an HTML error page, not JSON. JSON.parse on that body fails.

Errors from OAuth2 and the public info API are JSON, always in this shape:

{ "error": "invalid_grant" }

These responses are zstd-compressed (Content-Encoding: zstd), the same as success responses.

error HTTP Where Condition
invalid_request 400 token, info/oauth-client The form could not be parsed; client_id is missing
invalid_client 400 authorize, info/oauth-client client_id does not exist
invalid_client 401 token client_id does not exist, or a confidential client’s client_secret is wrong
invalid_redirect_uri 400 authorize redirect_uri is not in that client’s registered list
unsupported_grant_type 400 token grant_type is not authorization_code
invalid_grant 400 token The authorization code does not exist, was already used, or is expired, or client_id, redirect_uri, or PKCE does not match
too_many_tokens 403 token The user already has 25 tokens
server_error 500 token Internal server error

After redirect_uri is confirmed valid, errors from the authorization endpoint are not JSON. The server redirects to redirect_uri with error in the query:

error Condition
unsupported_response_type response_type is not code
invalid_request code_challenge is missing, or code_challenge_method is not S256
invalid_scope scope contains a permission name that does not exist
access_denied The user denied the request
server_error The server could not create an authorization code

When GET /api/v1/oauth2/userinfo fails, the response is 401 with an empty body, not the JSON above.

The strings below are the exact text the server sends. Match them exactly, or match a prefix. Stream protocol details are in real-time streams.

A request that fails at connect time does not start a stream. The body is plain text (with a trailing newline).

HTTP Body Condition
400 topics required, e.g. ?topics=… The multiplexed entry was called with no topics
400 unknown topic: <name> The topic name does not exist (it is case-sensitive)
401 invalid token A token was sent, and it is invalid or revoked. Public topics are rejected too
401 unauthorized A topic that requires a permission was requested with no token
403 forbidden: token scope missing The token does not have that topic’s permission
403 forbidden: not subscribed The account is not subscribed to that resource
429 quota exceeded That resource’s quota for today is used up
503 auth service unavailable The auth service is temporarily unreachable
405 method not allowed The method is not GET

On the multiplexed entry (/api/v1/trem/sse?topics=), when every topic is rejected, the body appends : <topic>, for example forbidden: not subscribed: <topic>. If at least one topic is accepted, the connection succeeds (200). Rejected topics and their reasons are in the denied field of the opening event: info. The reason string is one of unauthorized, forbidden: token scope missing, forbidden: not subscribed, or quota exceeded, with no topic name appended.

Event data Condition What to do
unsubscribed {"topic":"<topic>","reason":"quota exceeded"} (JSON) A topic that requires a permission went over quota, and the connection still has other topics That topic stops. The others continue
close quota exceeded (plain text) The last topic on the connection was removed for going over quota Do not reconnect immediately. Wait until the quota recovers
close token revoked (plain text) Any token on the account was revoked. About 5 seconds later, every authenticated connection for that account closes Reconnect with a token that is still valid. The revoked token gets invalid token

If a write times out (nothing can be written for about 30 seconds), the server drops the connection and does not send a close event. Treat a disconnect with no close as a network problem, and reconnect using retry: 3000 (3 seconds).