TarifsGitHubTableau de bord

Public API

Endpoints

Endpoints are your JSON-RPC URLs (a UUID in the URL path), the same resources as in the cabinet. An endpoint is identified by its uuid everywhere: in the path of usage / status / allowed-ips and in responses. The Public API never shows numeric ids, so endpoint objects have no id, network_id, network_environment_id, or plan_id, and a network is named by its slug. All routes require signed headers (see Authentication). Base URL: see Overview. There is no DELETE: pause an endpoint via status instead. Success responses use ok: true plus the fields below; a field without a value is left out of the JSON (it is never null).

RPC endpointsUUIDJSON-RPCendpointsHTTP

← Public API

Authentication

Every request needs X-Api-Key, X-Timestamp, and X-Signature (HMAC-SHA256); the signing scheme is described in Authentication.

Sign the request target exactly as you send it: the path and, when present, the ?query (the query string is part of the signed text). For POST/PATCH, hash the raw JSON body bytes you send; for GET use the empty body.

Endpoints belong to the billing workspace owner: a key acts on the workspace owner's endpoints and plan limits.

Errors

Failures use ok: false with error_code and error. A request that cannot be accepted (bad query value, bad JSON body, missing or unknown field) gets 400 and an errors array that names every wrong field: { field, error_code, error }. With one wrong field the top-level error_code is that field's code, with several it is common.validation_failed.

Unknown fields in a body are rejected, not ignored: a typo or a removed field (for example network_id) is reported as "… is not a field of this request".

Typical statuses: 400 validation, 401 / 403 / 429 from authentication and rate limits (see Authentication), 403 endpoint.network_not_allowed and endpoint.limit_reached (plan), 404 endpoint.not_found / endpoint.network_not_found / endpoint.environment_not_found, 503 endpoint.no_upstream, 500 endpoint.gateway_sync_failed and endpoint.host_prefix_exhausted.

Validation error example

{
  "ok": false,
  "error_code": "common.validation_failed",
  "error": "Validation failed",
  "errors": [
    { "field": "name", "error_code": "endpoint.name_required", "error": "Name is required." },
    { "field": "env_slug", "error_code": "endpoint.env_slug_required", "error": "env_slug is required (for example mainnet)." }
  ]
}

GET /v1/endpoints

List the JSON-RPC endpoints of the account (paginated).

Paramètres de requête

NomTypeDescription
pageintegerPage number, 1 to 10000 (default 1). (facultatif)
per_pageintegerPage size, 1 to 100 (default 30). (facultatif)

Champs de la réponse

NomTypeDescription
endpointsobject[]Endpoint objects of this page.
endpoints[].uuidstringEndpoint identifier. Use it in the path of usage / status / allowed-ips; it is also the last segment of the JSON-RPC URL.
endpoints[].namestringDisplay name.
endpoints[].statusstringactive | paused.
endpoints[].network_namestringNetwork display name.
endpoints[].network_slugstringNetwork slug (e.g. ethereum).
endpoints[].env_slugstringEnvironment slug (e.g. mainnet).
endpoints[].urlstringPublic JSON-RPC URL: https://[{host_prefix}-]{network_slug}-{env_slug}.{domain}/{uuid} (the host prefix is optional). The only address the Public API returns; gRPC / WebSocket addresses are not included.
endpoints[].rps_limitintegerRPS limit of your plan applied to this endpoint (0 if the account has no plan).
endpoints[].created_atstringISO-8601 timestamp with offset (UTC).
endpoints[].versionstringNode version of the environment. Optional: absent when unknown. (facultatif)
endpoints[].block_heightintegerLatest known block height of the environment. Optional: absent when unknown. (facultatif)
endpoints[].allowed_ipsstring[]Client IP allowlist of the endpoint. Empty array = no restriction. Always present.
pagination.pageintegerCurrent page.
pagination.per_pageintegerPage size.
pagination.totalintegerTotal endpoints.
pagination.has_morebooleanTrue if more pages exist.
  • Out-of-range or non-numeric page / per_page are rejected with 400 and errors[] naming the parameter; they are not clamped.
  • The query string is part of the signed path.

Exemple de requête

import { createHash, createHmac } from 'node:crypto'

const API_KEY = process.env.PUBLIC_API_KEY! // cabinet UUID
const SECRET = process.env.PUBLIC_API_SECRET! // sk_live_…
const BASE = 'https://api.rpcnode.dev'

// path: as the server receives it, with the query; body: the exact string that is sent
async function call(method: string, path: string, body = '') {
  const timestamp = Math.floor(Date.now() / 1000)
  const bodyHash = createHash('sha256').update(body).digest('hex')
  const signature = createHmac('sha256', SECRET)
    .update([timestamp, method, path, bodyHash].join('\n'))
    .digest('hex')
  const res = await fetch(BASE + path, {
    method,
    headers: {
      'X-Api-Key': API_KEY,
      'X-Timestamp': String(timestamp),
      'X-Signature': signature,
      ...(body ? { 'Content-Type': 'application/json' } : {}),
    },
    body: body || undefined,
  })
  return res.json()
}

console.log(await call('GET', '/v1/endpoints'))

La requête est signée dans l’extrait lui-même — voir Authentification pour les règles de signature.

Exemple de réponse

{
  "ok": true,
  "endpoints": [
    {
      "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "name": "Prod Ethereum",
      "status": "active",
      "network_name": "Ethereum",
      "network_slug": "ethereum",
      "env_slug": "mainnet",
      "url": "https://ethereum-mainnet.rpcnode.dev/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
      "rps_limit": 50,
      "created_at": "2026-08-01T12:00:00Z",
      "version": "v1.14.0",
      "block_height": 21000000,
      "allowed_ips": []
    }
  ],
  "pagination": {
    "page": 1,
    "per_page": 30,
    "total": 1,
    "has_more": false
  }
}

GET /v1/endpoints/available-networks

Networks (and their environment slugs) an endpoint can be created on under your plan.

Champs de la réponse

NomTypeDescription
networks[].slugstringNetwork slug (value for network_slug at create).
networks[].namestringNetwork display name.
networks[].environment_kindsstring[]Environment slugs available for create (e.g. mainnet, sepolia). Value for env_slug.
network_limitintegerMaximum number of networks your plan allows. Optional: absent when unlimited. (facultatif)
allowed_network_slugsstring[]Plan allowlist of network slugs. Optional: absent when the plan has no allowlist. (facultatif)
  • Use networks[].slug as network_slug and one of networks[].environment_kinds as env_slug in the create call.
  • Numeric network ids are not returned; the list is already filtered by your plan.

Exemple de requête

import { createHash, createHmac } from 'node:crypto'

const API_KEY = process.env.PUBLIC_API_KEY! // cabinet UUID
const SECRET = process.env.PUBLIC_API_SECRET! // sk_live_…
const BASE = 'https://api.rpcnode.dev'

// path: as the server receives it, with the query; body: the exact string that is sent
async function call(method: string, path: string, body = '') {
  const timestamp = Math.floor(Date.now() / 1000)
  const bodyHash = createHash('sha256').update(body).digest('hex')
  const signature = createHmac('sha256', SECRET)
    .update([timestamp, method, path, bodyHash].join('\n'))
    .digest('hex')
  const res = await fetch(BASE + path, {
    method,
    headers: {
      'X-Api-Key': API_KEY,
      'X-Timestamp': String(timestamp),
      'X-Signature': signature,
      ...(body ? { 'Content-Type': 'application/json' } : {}),
    },
    body: body || undefined,
  })
  return res.json()
}

console.log(await call('GET', '/v1/endpoints/available-networks'))

La requête est signée dans l’extrait lui-même — voir Authentification pour les règles de signature.

Exemple de réponse

{
  "ok": true,
  "networks": [
    {
      "slug": "ethereum",
      "name": "Ethereum",
      "environment_kinds": ["mainnet", "sepolia"]
    }
  ]
}

POST /v1/endpoints

Create a JSON-RPC endpoint on a network environment. Counts against the endpoints_limit of your plan.

Corps de la requête

NomTypeDescription
namestringDisplay name (required, max 255 characters; surrounding spaces are trimmed).
network_slugstringNetwork slug from GET /v1/endpoints/available-networks, e.g. ethereum.
env_slugstringEnvironment slug from networks[].environment_kinds, e.g. mainnet (case-insensitive).

Champs de la réponse

NomTypeDescription
endpointobjectThe created endpoint.
endpoint.uuidstringEndpoint identifier. Use it in the path of usage / status / allowed-ips; it is also the last segment of the JSON-RPC URL.
endpoint.namestringDisplay name.
endpoint.statusstringactive | paused.
endpoint.network_namestringNetwork display name.
endpoint.network_slugstringNetwork slug (e.g. ethereum).
endpoint.env_slugstringEnvironment slug (e.g. mainnet).
endpoint.urlstringPublic JSON-RPC URL: https://[{host_prefix}-]{network_slug}-{env_slug}.{domain}/{uuid} (the host prefix is optional). The only address the Public API returns; gRPC / WebSocket addresses are not included.
endpoint.rps_limitintegerRPS limit of your plan applied to this endpoint (0 if the account has no plan).
endpoint.created_atstringISO-8601 timestamp with offset (UTC).
endpoint.versionstringNode version of the environment. Optional: absent when unknown. (facultatif)
endpoint.block_heightintegerLatest known block height of the environment. Optional: absent when unknown. (facultatif)
endpoint.allowed_ipsstring[]Client IP allowlist of the endpoint. Empty array = no restriction. Always present.
  • Example body: {"name":"Prod Ethereum","network_slug":"ethereum","env_slug":"mainnet"}
  • No other fields are accepted: network_id and network_environment_id do not exist in the Public API and are rejected as unknown fields.
  • Errors: endpoint.name_required / endpoint.name_too_long / endpoint.network_required / endpoint.env_slug_required (400, listed in errors[]); endpoint.network_not_found and endpoint.environment_not_found (404); endpoint.no_plan (400); endpoint.network_not_allowed (403, network is not on your plan); endpoint.limit_reached (403, endpoints_limit of the plan reached); endpoint.no_upstream (503, the environment is temporarily unavailable).
  • Success is HTTP 200 (not 201) and returns the created endpoint.

Exemple de requête

import { createHash, createHmac } from 'node:crypto'

const API_KEY = process.env.PUBLIC_API_KEY! // cabinet UUID
const SECRET = process.env.PUBLIC_API_SECRET! // sk_live_…
const BASE = 'https://api.rpcnode.dev'

// path: as the server receives it, with the query; body: the exact string that is sent
async function call(method: string, path: string, body = '') {
  const timestamp = Math.floor(Date.now() / 1000)
  const bodyHash = createHash('sha256').update(body).digest('hex')
  const signature = createHmac('sha256', SECRET)
    .update([timestamp, method, path, bodyHash].join('\n'))
    .digest('hex')
  const res = await fetch(BASE + path, {
    method,
    headers: {
      'X-Api-Key': API_KEY,
      'X-Timestamp': String(timestamp),
      'X-Signature': signature,
      ...(body ? { 'Content-Type': 'application/json' } : {}),
    },
    body: body || undefined,
  })
  return res.json()
}

console.log(await call('POST', '/v1/endpoints', '{"name":"string","network_slug":"string","env_slug":"string"}'))

La requête est signée dans l’extrait lui-même — voir Authentification pour les règles de signature.

Exemple de réponse

{
  "ok": true,
  "endpoint": {
    "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "name": "Prod Ethereum",
    "status": "active",
    "network_name": "Ethereum",
    "network_slug": "ethereum",
    "env_slug": "mainnet",
    "url": "https://ethereum-mainnet.rpcnode.dev/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "rps_limit": 50,
    "created_at": "2026-08-01T12:00:00Z",
    "version": "v1.14.0",
    "block_height": 21000000,
    "allowed_ips": []
  }
}

GET /v1/endpoints/{uuid}/usage

Requests and credits of one endpoint for the last N calendar days, with top methods and webhook totals.

Paramètres de requête

NomTypeDescription
daysintegerWindow length: 7, 30 or 90. Default 30. Any other value is rejected with 400 (not replaced by the default). (facultatif)

Champs de la réponse

NomTypeDescription
usage.daysintegerWindow length: 7, 30 or 90.
usage.total_requestsintegerRequests in the window.
usage.total_creditsintegerCredits in the window.
usage.series[]object[]One point per calendar day (UTC), oldest first, exactly `days` points; days without usage are zeros.
usage.series[].datestringYYYY-MM-DD.
usage.series[].requestsintegerRequests that day.
usage.series[].creditsintegerCredits that day.
usage.methods[]object[]Top 20 methods of the window, sorted by credits (then requests) descending. Webhook deliveries appear here as webhook:* rows (e.g. webhook:block).
usage.methods[].methodstringRPC method name (or webhook:* kind).
usage.methods[].requestsintegerRequests for that method.
usage.methods[].creditsintegerCredits for that method.
usage.webhooksobjectWebhook usage over the whole window (not limited to the top 20 methods). All counters are 0 when there is none.
usage.webhooks.requestsintegerWebhook deliveries, all kinds.
usage.webhooks.creditsintegerCredits for webhook deliveries, all kinds.
usage.webhooks.address_watch_requestsintegerAddress Watch webhook deliveries.
usage.webhooks.address_watch_creditsintegerCredits for Address Watch webhooks.
usage.webhooks.block_requestsintegerBlock webhook deliveries.
usage.webhooks.block_creditsintegerCredits for block webhooks.
usage.webhooks.payment_requestsintegerPayment webhook deliveries.
usage.webhooks.payment_creditsintegerCredits for payment webhooks.
usage.webhooks.tx_watch_requestsintegerTx Watch webhook deliveries.
usage.webhooks.tx_watch_creditsintegerCredits for Tx Watch webhooks.
  • {uuid} is the endpoint uuid from list / create (not a numeric id). An unknown uuid, a malformed one, or an endpoint of another account gives 404 endpoint.not_found.
  • The query string is part of the signed path.
  • There is no account-wide endpoints usage route; for the account total across all endpoints see Account (GET /v1/usage).

Exemple de requête

import { createHash, createHmac } from 'node:crypto'

const API_KEY = process.env.PUBLIC_API_KEY! // cabinet UUID
const SECRET = process.env.PUBLIC_API_SECRET! // sk_live_…
const BASE = 'https://api.rpcnode.dev'

// path: as the server receives it, with the query; body: the exact string that is sent
async function call(method: string, path: string, body = '') {
  const timestamp = Math.floor(Date.now() / 1000)
  const bodyHash = createHash('sha256').update(body).digest('hex')
  const signature = createHmac('sha256', SECRET)
    .update([timestamp, method, path, bodyHash].join('\n'))
    .digest('hex')
  const res = await fetch(BASE + path, {
    method,
    headers: {
      'X-Api-Key': API_KEY,
      'X-Timestamp': String(timestamp),
      'X-Signature': signature,
      ...(body ? { 'Content-Type': 'application/json' } : {}),
    },
    body: body || undefined,
  })
  return res.json()
}

console.log(await call('GET', '/v1/endpoints/00000000-0000-0000-0000-000000000000/usage'))

La requête est signée dans l’extrait lui-même — voir Authentification pour les règles de signature.

Exemple de réponse

{
  "ok": true,
  "usage": {
    "days": 7,
    "total_requests": 100,
    "total_credits": 250,
    "series": [
      { "date": "2026-07-31", "requests": 0, "credits": 0 },
      { "date": "2026-08-06", "requests": 100, "credits": 250 }
    ],
    "methods": [
      { "method": "eth_getBalance", "requests": 50, "credits": 50 }
    ],
    "webhooks": {
      "requests": 0,
      "credits": 0,
      "address_watch_requests": 0,
      "address_watch_credits": 0,
      "block_requests": 0,
      "block_credits": 0,
      "payment_requests": 0,
      "payment_credits": 0,
      "tx_watch_requests": 0,
      "tx_watch_credits": 0
    }
  }
}

PATCH /v1/endpoints/{uuid}/status

Set the endpoint status to active or paused (paused stops JSON-RPC traffic).

Corps de la requête

NomTypeDescription
statusstringactive | paused (case-insensitive).

Champs de la réponse

NomTypeDescription
endpointobjectThe updated endpoint.
endpoint.uuidstringEndpoint identifier. Use it in the path of usage / status / allowed-ips; it is also the last segment of the JSON-RPC URL.
endpoint.namestringDisplay name.
endpoint.statusstringactive | paused.
endpoint.network_namestringNetwork display name.
endpoint.network_slugstringNetwork slug (e.g. ethereum).
endpoint.env_slugstringEnvironment slug (e.g. mainnet).
endpoint.urlstringPublic JSON-RPC URL: https://[{host_prefix}-]{network_slug}-{env_slug}.{domain}/{uuid} (the host prefix is optional). The only address the Public API returns; gRPC / WebSocket addresses are not included.
endpoint.rps_limitintegerRPS limit of your plan applied to this endpoint (0 if the account has no plan).
endpoint.created_atstringISO-8601 timestamp with offset (UTC).
endpoint.versionstringNode version of the environment. Optional: absent when unknown. (facultatif)
endpoint.block_heightintegerLatest known block height of the environment. Optional: absent when unknown. (facultatif)
endpoint.allowed_ipsstring[]Client IP allowlist of the endpoint. Empty array = no restriction. Always present.
  • Example body: {"status":"paused"}
  • Missing or other status values give 400 endpoint.invalid_status. An unknown uuid gives 404 endpoint.not_found (checked before the body).
  • There is no DELETE — use paused to stop traffic.

Exemple de requête

import { createHash, createHmac } from 'node:crypto'

const API_KEY = process.env.PUBLIC_API_KEY! // cabinet UUID
const SECRET = process.env.PUBLIC_API_SECRET! // sk_live_…
const BASE = 'https://api.rpcnode.dev'

// path: as the server receives it, with the query; body: the exact string that is sent
async function call(method: string, path: string, body = '') {
  const timestamp = Math.floor(Date.now() / 1000)
  const bodyHash = createHash('sha256').update(body).digest('hex')
  const signature = createHmac('sha256', SECRET)
    .update([timestamp, method, path, bodyHash].join('\n'))
    .digest('hex')
  const res = await fetch(BASE + path, {
    method,
    headers: {
      'X-Api-Key': API_KEY,
      'X-Timestamp': String(timestamp),
      'X-Signature': signature,
      ...(body ? { 'Content-Type': 'application/json' } : {}),
    },
    body: body || undefined,
  })
  return res.json()
}

console.log(await call('PATCH', '/v1/endpoints/00000000-0000-0000-0000-000000000000/status', '{"status":"string"}'))

La requête est signée dans l’extrait lui-même — voir Authentification pour les règles de signature.

Exemple de réponse

{
  "ok": true,
  "endpoint": {
    "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "name": "Prod Ethereum",
    "status": "paused",
    "network_name": "Ethereum",
    "network_slug": "ethereum",
    "env_slug": "mainnet",
    "url": "https://ethereum-mainnet.rpcnode.dev/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "rps_limit": 50,
    "created_at": "2026-08-01T12:00:00Z",
    "version": "v1.14.0",
    "block_height": 21000000,
    "allowed_ips": []
  }
}

PATCH /v1/endpoints/{uuid}/allowed-ips

Replace the client IP allowlist of the endpoint. An empty array clears the restriction (allow all).

Corps de la requête

NomTypeDescription
allowed_ipsstring[]Full list of IPv4 / IPv6 literal addresses (max 50). The key is required; [] clears the list. CIDR ranges and hostnames are not accepted.

Champs de la réponse

NomTypeDescription
endpointobjectThe updated endpoint.
endpoint.uuidstringEndpoint identifier. Use it in the path of usage / status / allowed-ips; it is also the last segment of the JSON-RPC URL.
endpoint.namestringDisplay name.
endpoint.statusstringactive | paused.
endpoint.network_namestringNetwork display name.
endpoint.network_slugstringNetwork slug (e.g. ethereum).
endpoint.env_slugstringEnvironment slug (e.g. mainnet).
endpoint.urlstringPublic JSON-RPC URL: https://[{host_prefix}-]{network_slug}-{env_slug}.{domain}/{uuid} (the host prefix is optional). The only address the Public API returns; gRPC / WebSocket addresses are not included.
endpoint.rps_limitintegerRPS limit of your plan applied to this endpoint (0 if the account has no plan).
endpoint.created_atstringISO-8601 timestamp with offset (UTC).
endpoint.versionstringNode version of the environment. Optional: absent when unknown. (facultatif)
endpoint.block_heightintegerLatest known block height of the environment. Optional: absent when unknown. (facultatif)
endpoint.allowed_ipsstring[]Client IP allowlist of the endpoint. Empty array = no restriction. Always present.
  • Example body: {"allowed_ips":["203.0.113.10","2001:db8::1"]}
  • Clear the restriction: {"allowed_ips":[]}
  • The list replaces the previous one. Blank entries are skipped, duplicates are removed, and addresses are returned in canonical form, so an IPv6 address may come back written out in full (2001:db8:0:0:0:0:0:1).
  • Errors: missing allowed_ips gives 400 with errors[]; endpoint.too_many_ips (more than 50) and endpoint.invalid_ip (not an IP literal) give 400; an unknown uuid gives 404 endpoint.not_found (checked before the body).

Exemple de requête

import { createHash, createHmac } from 'node:crypto'

const API_KEY = process.env.PUBLIC_API_KEY! // cabinet UUID
const SECRET = process.env.PUBLIC_API_SECRET! // sk_live_…
const BASE = 'https://api.rpcnode.dev'

// path: as the server receives it, with the query; body: the exact string that is sent
async function call(method: string, path: string, body = '') {
  const timestamp = Math.floor(Date.now() / 1000)
  const bodyHash = createHash('sha256').update(body).digest('hex')
  const signature = createHmac('sha256', SECRET)
    .update([timestamp, method, path, bodyHash].join('\n'))
    .digest('hex')
  const res = await fetch(BASE + path, {
    method,
    headers: {
      'X-Api-Key': API_KEY,
      'X-Timestamp': String(timestamp),
      'X-Signature': signature,
      ...(body ? { 'Content-Type': 'application/json' } : {}),
    },
    body: body || undefined,
  })
  return res.json()
}

console.log(await call('PATCH', '/v1/endpoints/00000000-0000-0000-0000-000000000000/allowed-ips', '{"allowed_ips":[]}'))

La requête est signée dans l’extrait lui-même — voir Authentification pour les règles de signature.

Exemple de réponse

{
  "ok": true,
  "endpoint": {
    "uuid": "a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "name": "Prod Ethereum",
    "status": "active",
    "network_name": "Ethereum",
    "network_slug": "ethereum",
    "env_slug": "mainnet",
    "url": "https://ethereum-mainnet.rpcnode.dev/a1b2c3d4-e5f6-7890-abcd-ef1234567890",
    "rps_limit": 50,
    "created_at": "2026-08-01T12:00:00Z",
    "version": "v1.14.0",
    "block_height": 21000000,
    "allowed_ips": ["203.0.113.10"]
  }
}

Conseils

  • Sign every request with X-Api-Key, X-Timestamp, and X-Signature (see Authentication); the query string is part of the signed path.
  • Get network_slug and env_slug from GET /v1/endpoints/available-networks before creating an endpoint.
  • JSON-RPC calls go to endpoint.url — do not send Public API HMAC headers on those URLs.
  • There is no DELETE — use PATCH …/status with paused to stop traffic.
  • Plan caps: endpoints_limit (count) is checked at create; rps_limit applies per endpoint. See Account for your limits.
  • Read an absent optional field (version, block_height, network_limit, allowed_network_slugs) as "no value", not as null.
  • The Public API itself is rate limited per account (see Authentication); this is separate from the endpoint rps_limit.