Error Codes Overview of all API error codes and their meanings.
Errors are returned in the following format:
JSON {
"error" : {
"code" : "validation_error" ,
"message" : "Human-readable error message" ,
"details" : [
{
"field" : "email" ,
"message" : "Must be a valid email address"
}
]
}
}
HTTP Status Codes Status Meaning Description 200 OK Request successful 201 Created Resource created 204 No Content Successful, no data 400 Bad Request Invalid request data 401 Unauthorized Authentication missing/invalid 402 Payment Required Plan limit exceeded 403 Forbidden No permission 404 Not Found Resource not found 409 Conflict Resource already exists 422 Unprocessable Validation error 429 Too Many Requests Rate limit exceeded 500 Internal Error Server error 503 Service Unavailable Service temporarily unavailable
Error Codes Authentication Errors (401) Code Description Solution unauthorized No auth header/cookie Add X-API-Key header or session cookie invalid_token Token invalid Request a new token token_expired Token expired Refresh the token invalid_api_key API key invalid Check key or create a new one api_key_revoked API key revoked Create a new key magic_link_expired Magic link token has expired Request a new magic link magic_link_already_used Magic link was already consumed Request a new magic link invalid_session Session cookie is invalid or expired Re-authenticate via magic link
JSON {
"error" : {
"code" : "magic_link_expired" ,
"message" : "This magic link has expired. Please request a new one."
}
}
Authorization Errors (403) Code Description Solution forbidden No permission Check permissions insufficient_permissions Missing permissions Use an API key with broader scopes organization_mismatch Wrong organization Resource belongs to another org organization_not_found Organization ID not valid Check X-Organization-Id header not_a_member User is not a member of the org Request access from an org owner owner_only Action restricted to owners Only organization owners can access billing/settings
JSON {
"error" : {
"code" : "not_a_member" ,
"message" : "You are not a member of this organization"
}
}
Payment Required Errors (402) These errors occur when an organization exceeds the limits of their subscription tier.
Code Description Solution subscriber_limit_exceeded Max subscriber count reached Upgrade plan or remove inactive subscribers email_limit_exceeded Monthly email limit reached Upgrade plan or wait for monthly reset
JSON {
"error" : {
"code" : "subscriber_limit_exceeded" ,
"message" : "Subscriber limit exceeded" ,
"limit" : "max_subscribers" ,
"current" : 500 ,
"max" : 500 ,
"tier" : "free" ,
"upgrade_url" : "/api/v1/billing/create-checkout-session"
}
}
Tier Limits
Tier Subscribers Emails/Month Free 500 1,000 Starter 5,000 10,000 Professional 50,000 100,000
Billing Errors (400/402) Code Description Solution no_active_subscription Organization has no paid subscription Create a checkout session first invalid_price Invalid Stripe price ID Use a valid price ID from your Stripe dashboard checkout_failed Stripe checkout session creation failed Retry or check Stripe status portal_unavailable Cannot create Stripe portal Ensure org has a Stripe customer ID
JSON {
"error" : {
"code" : "no_active_subscription" ,
"message" : "No active subscription found. Please subscribe to a plan first."
}
}
Validation Errors (400/422) Code Description Solution validation_error Validation failed Correct the fields invalid_email Email invalid Use a valid email address invalid_tag Tag does not exist Check the tag name missing_field Required field missing Add the field
JSON {
"error" : {
"code" : "validation_error" ,
"message" : "Validation failed" ,
"details" : [
{
"field" : "email" ,
"message" : "Must be a valid email address" ,
"code" : "invalid_email"
},
{
"field" : "first_name" ,
"message" : "Must not exceed 100 characters" ,
"code" : "max_length"
}
]
}
}
Resource Errors (404/409) Code Description Solution not_found Resource not found Check the ID subscriber_not_found Subscriber not found Check the subscriber ID tag_not_found Tag not found Check the tag name/ID organization_not_found Organization not found Check the organization ID already_exists Resource already exists Observe unique constraint email_exists Email already exists Use a different email or update
JSON {
"error" : {
"code" : "subscriber_not_found" ,
"message" : "Subscriber with ID 'xxx' not found"
}
}
Rate Limit Errors (429) Code Description Solution rate_limited Rate limit reached Wait or upgrade
JSON {
"error" : {
"code" : "rate_limited" ,
"message" : "Rate limit exceeded. Retry after 60 seconds" ,
"retry_after" : 60
}
}
Response Headers:
Text Only X-RateLimit-Limit: 1000
X-RateLimit-Remaining: 0
X-RateLimit-Reset: 1609459200
Retry-After: 60
Server Errors (5xx) Code Description Solution internal_error Internal error Retry with backoff database_error Database error Retry with backoff external_service_error External service error Retry with backoff service_unavailable Service unavailable Try again later
JSON {
"error" : {
"code" : "internal_error" ,
"message" : "An unexpected error occurred" ,
"request_id" : "req_abc123"
}
}
Request ID
A request_id is returned for server errors. Include it when contacting support.
Error Handling Best Practices Python Python from subscribeflow import Client
from subscribeflow.exceptions import (
SubscribeFlowError ,
ValidationError ,
NotFoundError ,
RateLimitError ,
LimitExceededError ,
)
client = Client ( api_key = "..." )
try :
subscriber = client . subscribers . create ( email = "invalid" )
except LimitExceededError as e :
print ( f "Limit exceeded: { e . limit } ( { e . current } / { e . max } )" )
print ( f "Upgrade at: { e . upgrade_url } " )
except ValidationError as e :
print ( f "Validation failed: { e . details } " )
except NotFoundError as e :
print ( f "Not found: { e . message } " )
except RateLimitError as e :
print ( f "Rate limited. Retry after { e . retry_after } s" )
except SubscribeFlowError as e :
print ( f "Error: { e . code } - { e . message } " )
TypeScript TypeScript import { SubscribeFlow , SubscribeFlowError } from '@subscribeflow/sdk' ;
const client = new SubscribeFlow ( '...' );
try {
const subscriber = await client . subscribers . create ({ email : 'invalid' });
} catch ( error ) {
if ( error instanceof SubscribeFlowError ) {
switch ( error . code ) {
case 'subscriber_limit_exceeded' :
case 'email_limit_exceeded' :
console . log ( `Plan limit reached: ${ error . current } / ${ error . max } ` );
break ;
case 'validation_error' :
console . log ( 'Validation failed:' , error . details );
break ;
case 'rate_limited' :
console . log ( `Retry after ${ error . retryAfter } s` );
break ;
default :
console.log ( `Error: ${ error . code } - ${ error . message } ` );
}
}
}
Retry Strategy Python import time
from subscribeflow.exceptions import RateLimitError , ServerError
def with_retry ( func , max_retries = 3 ):
for attempt in range ( max_retries ):
try :
return func ()
except RateLimitError as e :
time . sleep ( e . retry_after )
except ServerError :
time . sleep ( 2 ** attempt ) # Exponential backoff
raise Exception ( "Max retries exceeded" )