Error Codes and Limits
Error Response Format
Section titled “Error Response Format”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 usecodefor 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"}List of Error Codes
Section titled “List of Error Codes”| code | HTTP | Meaning | How to resolve |
|---|---|---|---|
MISSING_FIELDS | 400 | A required field is missing | Check the required items in the request body |
INVALID_URL | 400 | url / icon / badge / endpoint does not start with https:// | Fix the target field to be an https:// URL |
INVALID_ENDPOINT | 400 | The format of endpoint is invalid | Send the endpoint obtained from the browser’s Push API as-is |
TOO_MANY_ENDPOINTS | 400 | The endpoints in a batch send exceeds the limit (100) | Split into batches of 100 or fewer before sending |
INVALID_EMAIL | 400 | The email address format is invalid (auth-related) | Check the format of the email address |
INVALID_INPUT | 400 | The input value is invalid (auth-related; e.g. the password is too long) | Check the format and length of the input value |
INVALID_SCOPE | 400 | The scope name specified when creating the API key is invalid | Specify one of subscribe_only / notify / full |
MISSING_CODE | 400 | The GitHub OAuth callback is missing the code parameter | Restart the OAuth flow from the beginning |
UNAUTHORIZED | 401 | The authentication header is missing or invalid | Add the Authorization: Bearer <API key> header |
INVALID_API_KEY | 401 | The API key is invalid | Check the key value, or reissue it from the dashboard |
INVALID_SESSION | 401 | The session is invalid | Log in again |
INVALID_CREDENTIALS | 401 | The email address or password is incorrect | Check your input |
INSUFFICIENT_SCOPE | 403 | The API key’s scope is insufficient | Use a key with a higher scope (notify / full) |
APP_MISMATCH | 403 | The API key does not match the appId in the URL | Check the appId in the URL and the app that issued the API key you are using |
PLAN_LIMIT_EXCEEDED | 403 | The Free plan’s app count limit (1) has been exceeded | Delete an existing app, or upgrade your plan |
INVALID_STATE | 403 | The GitHub OAuth state parameter does not match | Restart the OAuth flow from the beginning |
NOT_FOUND | 404 | The specified resource does not exist | Check the specified values such as app ID and key ID |
EMAIL_IN_USE | 409 | The email address is already registered | Use a different email address, or log in |
PAYLOAD_TOO_LARGE | 413 | The send payload exceeds the limit (3,072 bytes) | Reduce the total size of title / body / url / icon / badge |
MONTHLY_LIMIT_EXCEEDED | 429 | The 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_EXCEEDED | 429 | The Free plan’s subscriber limit (1,000 subscribers) has been exceeded (includes upgrade_url) | Upgrade your plan, or trim your subscriber count |
RATE_LIMITED | 429 | The rate limit has been exceeded | Wait for the time in the Retry-After header (seconds) before retrying |
GITHUB_AUTH_FAILED | 502 | OAuth authentication with GitHub failed | Wait a while and try logging in again |
OAUTH_NOT_CONFIGURED | 503 | GitHub OAuth is not configured on the server side | Contact the administrator (GitHub login is unavailable) |
CONFIG_ERROR | 503 | The server-side configuration is incomplete (e.g. the encryption key or pepper is not set) | Contact the administrator |
INTERNAL_ERROR | 500 | An unhandled exception occurred | Wait a while and retry; if it persists, contact support |
Rate Limits
Section titled “Rate Limits”The following endpoints are rate-limited per IP address.
| Target | Limit |
|---|---|
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 endpoints | 20 times / 60 seconds |
When the limit is exceeded, HTTP 429 (code: "RATE_LIMITED") is returned with a Retry-After: 60 header attached.
Plan Limits (Free Plan)
Section titled “Plan Limits (Free Plan)”| Item | Limit | Error code on exceeding |
|---|---|---|
| App count | 1 | PLAN_LIMIT_EXCEEDED (403) |
| Subscriber count | 1,000 | SUBSCRIBER_LIMIT_EXCEEDED (429) |
| Monthly send count | 30,000 sends | MONTHLY_LIMIT_EXCEEDED (429) |
Whenever any limit is exceeded, the response includes the upgrade link upgrade_url (https://todoke.dev/#pricing).
Send Payload Limits
Section titled “Send Payload Limits”- Payload size: up to 3,072 bytes for the UTF-8 size of
title/body/url/icon/badgeencoded as JSON. Exceeding this returnsPAYLOAD_TOO_LARGE(413) - Batch send count: the
endpointsinPOST /api/v1/notify/batchcan be up to 100. Exceeding this returnsTOO_MANY_ENDPOINTS(400) - URL format:
url/icon/badge/endpointallowhttps://only. Specifyinghttp://returnsINVALID_URL(400)