Overview
The beaconcha.in API uses rate limiting to ensure fair usage and maintain service quality for all users. Rate limits vary by subscription tier and are enforced across multiple time windows.
Every API response includes headers that show your current rate limit status:
Header Description Example ratelimit-limitRequests allowed per window 1ratelimit-remainingRequests remaining in current window 0ratelimit-resetSeconds until the window resets 1ratelimit-windowThe time window type secondratelimit-validapikeyWhether your API key is valid true
The API also returns detailed limits for each time window:
Header Description x-ratelimit-limit-secondRequests allowed per second x-ratelimit-limit-minuteRequests allowed per minute x-ratelimit-limit-hourRequests allowed per hour x-ratelimit-limit-dayRequests allowed per day x-ratelimit-limit-monthRequests allowed per month x-ratelimit-remaining-secondRemaining requests this second x-ratelimit-remaining-minuteRemaining requests this minute x-ratelimit-remaining-hourRemaining requests this hour x-ratelimit-remaining-dayRemaining requests today x-ratelimit-remaining-monthRemaining requests this month
Checking Your Rate Limits
Make any API request and inspect the response headers to see your current limits:
curl -s -D - -o /dev/null --request POST \
--url https://beaconcha.in/api/v2/ethereum/validators/rewards-aggregate \
--header 'Authorization: Bearer <YOUR_API_KEY>' \
--header 'Content-Type: application/json' \
--data '{
"chain": "mainnet",
"validator": { "validator_identifiers": [1] },
"range": { "evaluation_window": "24h" }
}'
Example Response Headers: ratelimit-limit: 1
ratelimit-remaining: 0
ratelimit-reset: 1
ratelimit-window: second
ratelimit-validapikey: true
x-ratelimit-limit-second: 1
x-ratelimit-limit-minute: 1000
x-ratelimit-limit-hour: 1000
x-ratelimit-limit-day: 1000
x-ratelimit-limit-month: 1000
x-ratelimit-remaining-second: 0
x-ratelimit-remaining-minute: 972
x-ratelimit-remaining-hour: 972
x-ratelimit-remaining-day: 972
x-ratelimit-remaining-month: 972
import requests
API_KEY = "<YOUR_API_KEY>"
response = requests.post(
"https://beaconcha.in/api/v2/ethereum/validators/rewards-aggregate" ,
headers = {
"Authorization" : f "Bearer { API_KEY } " ,
"Content-Type" : "application/json"
},
json = {
"chain" : "mainnet" ,
"validator" : { "validator_identifiers" : [ 1 ]},
"range" : { "evaluation_window" : "24h" }
}
)
# Extract rate limit information
rate_limits = {
"per_second" : {
"limit" : response.headers.get( "x-ratelimit-limit-second" ),
"remaining" : response.headers.get( "x-ratelimit-remaining-second" )
},
"per_minute" : {
"limit" : response.headers.get( "x-ratelimit-limit-minute" ),
"remaining" : response.headers.get( "x-ratelimit-remaining-minute" )
},
"per_hour" : {
"limit" : response.headers.get( "x-ratelimit-limit-hour" ),
"remaining" : response.headers.get( "x-ratelimit-remaining-hour" )
},
"per_day" : {
"limit" : response.headers.get( "x-ratelimit-limit-day" ),
"remaining" : response.headers.get( "x-ratelimit-remaining-day" )
},
"per_month" : {
"limit" : response.headers.get( "x-ratelimit-limit-month" ),
"remaining" : response.headers.get( "x-ratelimit-remaining-month" )
}
}
print ( "Your API Rate Limits:" )
for window, limits in rate_limits.items():
print ( f " { window } : { limits[ 'remaining' ] } / { limits[ 'limit' ] } remaining" )
Example Output: Your API Rate Limits:
per_second: 0/1 remaining
per_minute: 972/1000 remaining
per_hour: 972/1000 remaining
per_day: 972/1000 remaining
per_month: 972/1000 remaining
const API_KEY = '<YOUR_API_KEY>' ;
async function checkRateLimits () {
const response = await fetch (
'https://beaconcha.in/api/v2/ethereum/validators/rewards-aggregate' ,
{
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ API_KEY } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ({
chain: 'mainnet' ,
validator: { validator_identifiers: [ 1 ] },
range: { evaluation_window: '24h' }
})
}
);
const rateLimits = {
perSecond: {
limit: response . headers . get ( 'x-ratelimit-limit-second' ),
remaining: response . headers . get ( 'x-ratelimit-remaining-second' )
},
perMinute: {
limit: response . headers . get ( 'x-ratelimit-limit-minute' ),
remaining: response . headers . get ( 'x-ratelimit-remaining-minute' )
},
perHour: {
limit: response . headers . get ( 'x-ratelimit-limit-hour' ),
remaining: response . headers . get ( 'x-ratelimit-remaining-hour' )
},
perDay: {
limit: response . headers . get ( 'x-ratelimit-limit-day' ),
remaining: response . headers . get ( 'x-ratelimit-remaining-day' )
},
perMonth: {
limit: response . headers . get ( 'x-ratelimit-limit-month' ),
remaining: response . headers . get ( 'x-ratelimit-remaining-month' )
}
};
console . log ( 'Your API Rate Limits:' );
for ( const [ window , limits ] of Object . entries ( rateLimits )) {
console . log ( ` ${ window } : ${ limits . remaining } / ${ limits . limit } remaining` );
}
return rateLimits ;
}
checkRateLimits ();
Handling Rate Limit Errors
When you exceed a rate limit, the API returns a 429 Too Many Requests status code. Implement retry logic with exponential backoff:
import requests
import time
def make_request_with_retry ( url , payload , max_retries = 5 ):
"""Make an API request with automatic retry on rate limit."""
for attempt in range (max_retries):
response = requests.post(
url,
headers = {
"Authorization" : f "Bearer { API_KEY } " ,
"Content-Type" : "application/json"
},
json = payload
)
if response.status_code == 429 :
# Get reset time from headers, default to exponential backoff
reset_seconds = int (response.headers.get( "ratelimit-reset" , 2 ** attempt))
print ( f "Rate limited. Waiting { reset_seconds } seconds..." )
time.sleep(reset_seconds)
continue
response.raise_for_status()
return response.json()
raise Exception ( "Max retries exceeded" )
async function makeRequestWithRetry ( url , payload , maxRetries = 5 ) {
for ( let attempt = 0 ; attempt < maxRetries ; attempt ++ ) {
const response = await fetch ( url , {
method: 'POST' ,
headers: {
'Authorization' : `Bearer ${ API_KEY } ` ,
'Content-Type' : 'application/json'
},
body: JSON . stringify ( payload )
});
if ( response . status === 429 ) {
// Get reset time from headers, default to exponential backoff
const resetSeconds = parseInt (
response . headers . get ( 'ratelimit-reset' ) || Math . pow ( 2 , attempt )
);
console . log ( `Rate limited. Waiting ${ resetSeconds } seconds...` );
await new Promise ( resolve => setTimeout ( resolve , resetSeconds * 1000 ));
continue ;
}
if ( ! response . ok ) throw new Error ( `HTTP ${ response . status } ` );
return await response . json ();
}
throw new Error ( 'Max retries exceeded' );
}
Rate Limits by Plan
Rate limits vary by subscription tier:
Plan Price/mo Features Limit/sec Limit/mo Free 0€ Basic 1 1,000 Hobbyist 59€* Basic 1 — Business 99€* Basic 2 — Scale 399€* Basic & Pro 💎 5 — Enterprise Contact us Basic & Pro 💎 Contact us —
*Prices shown are for annual billing, excluding VAT
Pro 💎 features include premium validator selectors (withdrawal address, deposit_address) and are available on Scale and Enterprise plans.
View current pricing at beaconcha.in/pricing . Your actual limits are always available in the response headers.
Best Practices
Monitor Headers Check x-ratelimit-remaining-* headers before making bulk requests to avoid hitting limits.
Use Backoff Implement exponential backoff when receiving 429 errors to gracefully handle rate limits.
Batch with Dashboards Use dashboard_id to query multiple validators in a single request instead of individual calls.