Skip to content

Error Codes and Limits

When the API returns an error, it returns a JSON body in the following format along with the HTTP status code.

{
"error": "Human-readable error message",
"code": "MACHINE_READABLE_CODE"
}
  • error: A human-readable message (in Japanese). You can display it as-is in the UI, but use code for branching logic.
  • code: A machine-readable code for programmatic checks. It is enumerated as a fixed set of values in the list on this page.
{
"error": "月間送信上限に達しました",
"code": "MONTHLY_LIMIT_EXCEEDED",
"upgrade_url": "https://todoke.dev/#pricing"
}
codeHTTPMeaningHow to resolve
MISSING_FIELDS400A required field is missingCheck the required items in the request body
INVALID_URL400url / icon / badge / endpoint does not start with https://Fix the target field to be an https:// URL
INVALID_ENDPOINT400The format of endpoint is invalidSend the endpoint obtained from the browser’s Push API as-is
TOO_MANY_ENDPOINTS400The endpoints in a batch send exceeds the limit (100)Split into batches of 100 or fewer before sending
INVALID_EMAIL400The email address format is invalid (auth-related)Check the format of the email address
INVALID_INPUT400The input value is invalid (auth-related; e.g. the password is too long)Check the format and length of the input value
INVALID_SCOPE400The scope name specified when creating the API key is invalidSpecify one of subscribe_only / notify / full
MISSING_CODE400The GitHub OAuth callback is missing the code parameterRestart the OAuth flow from the beginning
UNAUTHORIZED401The authentication header is missing or invalidAdd the Authorization: Bearer <API key> header
INVALID_API_KEY401The API key is invalidCheck the key value, or reissue it from the dashboard
INVALID_SESSION401The session is invalidLog in again
INVALID_CREDENTIALS401The email address or password is incorrectCheck your input
INSUFFICIENT_SCOPE403The API key’s scope is insufficientUse a key with a higher scope (notify / full)
APP_MISMATCH403The API key does not match the appId in the URLCheck the appId in the URL and the app that issued the API key you are using
PLAN_LIMIT_EXCEEDED403The Free plan’s app count limit (1) has been exceededDelete an existing app, or upgrade your plan
INVALID_STATE403The GitHub OAuth state parameter does not matchRestart the OAuth flow from the beginning
NOT_FOUND404The specified resource does not existCheck the specified values such as app ID and key ID
EMAIL_IN_USE409The email address is already registeredUse a different email address, or log in
PAYLOAD_TOO_LARGE413The send payload exceeds the limit (3,072 bytes)Reduce the total size of title / body / url / icon / badge
MONTHLY_LIMIT_EXCEEDED429The Free plan’s monthly send limit (30,000 sends) has been exceeded (includes upgrade_url)Upgrade your plan, or wait until next month
SUBSCRIBER_LIMIT_EXCEEDED429The Free plan’s subscriber limit (1,000 subscribers) has been exceeded (includes upgrade_url)Upgrade your plan, or trim your subscriber count
RATE_LIMITED429The rate limit has been exceededWait for the time in the Retry-After header (seconds) before retrying
GITHUB_AUTH_FAILED502OAuth authentication with GitHub failedWait a while and try logging in again
OAUTH_NOT_CONFIGURED503GitHub OAuth is not configured on the server sideContact the administrator (GitHub login is unavailable)
CONFIG_ERROR503The server-side configuration is incomplete (e.g. the encryption key or pepper is not set)Contact the administrator
INTERNAL_ERROR500An unhandled exception occurredWait a while and retry; if it persists, contact support

The following endpoints are rate-limited per IP address.

TargetLimit
Login (POST /auth/login)10 times / 60 seconds
GitHub OAuth start (GET /auth/github)10 times / 60 seconds (shares a counter with login)
User registration (POST /auth/register)5 times / 60 seconds
API key authentication endpoints20 times / 60 seconds

When the limit is exceeded, HTTP 429 (code: "RATE_LIMITED") is returned with a Retry-After: 60 header attached.

ItemLimitError code on exceeding
App count1PLAN_LIMIT_EXCEEDED (403)
Subscriber count1,000SUBSCRIBER_LIMIT_EXCEEDED (429)
Monthly send count30,000 sendsMONTHLY_LIMIT_EXCEEDED (429)

Whenever any limit is exceeded, the response includes the upgrade link upgrade_url (https://todoke.dev/#pricing).

  • Payload size: up to 3,072 bytes for the UTF-8 size of title / body / url / icon / badge encoded as JSON. Exceeding this returns PAYLOAD_TOO_LARGE (413)
  • Batch send count: the endpoints in POST /api/v1/notify/batch can be up to 100. Exceeding this returns TOO_MANY_ENDPOINTS (400)
  • URL format: url / icon / badge / endpoint allow https:// only. Specifying http:// returns INVALID_URL (400)