For the complete documentation index, see llms.txt. This page is also available as Markdown.

Límites de solicitudes

Cuánto cuesta una solicitud, qué significan los encabezados de límites de solicitudes, qué presupuestos son independientes entre sí y qué pasa cuando superas cada uno.

El Exchange no mide tu tráfico en solicitudes por segundo. Lo mide en peso por segundo. A cada solicitud se le cobra un costo: la mayoría cuesta una unidad y unas pocas cuestan más. Un cliente que regula su ritmo contando solicitudes recibe rechazos mientras su propio contador todavía parece estar bien. Esta es la sorpresa de integración más común con esta API.

Esta página explica el modelo: cuánto cuesta cada cosa, qué presupuestos están separados y cómo leer los encabezados. La definición normativa es el contrato OpenAPI, publicado en nexus-xyz/nexus-exchange-api y servido en /openapi.json. La sección "Rate limits" de la descripción de la API define la semántica de los encabezados, y cada operación lleva su propio costo legible por máquina. Cuando esta página y el contrato no coinciden, el contrato tiene razón.

Cuánto cuesta una solicitud

Tu presupuesto es un token bucket que se recarga de forma continua a la tasa por segundo de tu nivel, con una capacidad de exactamente un segundo de tokens. De esa capacidad se derivan dos consecuencias. La tasa sostenida y la ráfaga permitida son el mismo número, así que no hay una reserva de varios segundos que puedas acumular quedándote inactivo. Y remaining nunca puede superar a limit.

La mayoría de las solicitudes cuestan 1. Las excepciones:

Costo
Se aplica a

5

GET /account/activity, GET /account/portfolio-history, GET /account/summary, GET /fills, GET /orders/history, GET /positions/pnl, GET /stats/referrals

1 + floor(order_count / 40)

POST /orders/batch

1

todas las demás operaciones que documenta el contrato

Cada costo también se aplica a la gemela /api/v1 de la operación, cuando existe.

Las lecturas pesadas procesan o recorren un búfer grande por cuenta (ejecuciones, historial de órdenes, la serie de tiempo del portafolio, el feed de actividad combinado), se reparten entre las posiciones abiertas o agregan conteos de referidos de toda la plataforma desde el backend de cuentas. El costo de un envío en lote crece con su tamaño, así que hasta 40 órdenes cuestan lo mismo que una.

Así que quien llama con nivel Pro y limit: 20 obtiene 20 lecturas de ticker por segundo, o 4 lecturas de /fills, del mismo presupuesto en el mismo nivel. Regula tu ritmo según el peso, no según la cantidad de solicitudes.

Dos propiedades de seguridad acotan el peor caso. El cobro de una sola solicitud tiene un tope de un segundo de tokens, así que un lote demasiado grande nunca puede volverse permanentemente imposible de pagar. Consume el segundo completo una vez que el bucket se recarga y pasa, en lugar de quedar en un ciclo infinito de reintentos que nunca podría pagar. Y a un cuerpo de lote que el servidor no puede analizar se le cobra la unidad base en lugar de rechazarlo por el cálculo de peso.

En lugar de fijar en el código la tabla de arriba, lee el costo del contrato: las operaciones que cuestan más de una unidad llevan x-nexus-rate-limit-weight, y las que tienen un costo que depende del cuerpo también llevan x-nexus-rate-limit-weight-formula. Para una operación que documenta el contrato, la ausencia del marcador significa peso 1. Esa es la forma sobre la que hay que construir un limitador del lado del cliente.

La salvedad importa. El contrato enumera las operaciones admitidas, y la regla se cumple en todas ellas, pero no dice nada sobre otras rutas que casualmente respondan. Una ruta que el contrato no enumera no lleva ningún marcador, no está cubierta por la regla y puede cobrarse de forma distinta a lo que sugiere su ausencia. Construye sobre las operaciones que documenta el contrato, no sobre rutas encontradas sondeando. Esas operaciones son las que describen los pesos, y esta página.

Cuatro presupuestos, no uno

Hay cuatro clases de recursos independientes. Gastar una no gasta las demás, y cada una rechaza a su manera:

Clase
Cubre
Se cobra contra

Solicitudes

Todas las operaciones REST que no son escrituras de órdenes

Tus buckets de solicitudes por clave y por propietario

Acciones de trading

POST y PATCH bajo /orders, marcadas con x-nexus-rate-limit-class: trading

Un bucket de órdenes dedicado, del mismo tamaño por segundo que el bucket de solicitudes

Cancelaciones

DELETE bajo /orders, también marcadas con x-nexus-rate-limit-class: trading

Un bucket de cancelaciones separado, también del mismo tamaño por segundo

Plano de control de WebSocket

Conexiones, suscripciones y frames entrantes del cliente

Topes por nivel. Consulta Topes de WebSocket

Una escritura de orden se cobra al bucket de trading en lugar del bucket de solicitudes, no además de él. Gracias a esa separación, una ráfaga de polling no puede dejar sin recursos al envío de tus órdenes, y el flujo de órdenes no puede dejar sin recursos a tus lecturas. De ahí se sigue que un x-ratelimit-remaining holgado en tu última lectura no te dice nada sobre tu holgura para enviar órdenes. Son buckets distintos, así que consulta el bucket del que estás por gastar. GET /account/rate-limit informa cada presupuesto por separado en buckets, y buckets.order responde si puedes enviar una orden.

Las cancelaciones nunca comparten un token con los envíos

Cancelar es la única acción que se separa del trading en un presupuesto propio. Un DELETE en los endpoints de órdenes (cancelar una, cancelar todas o cancelar por mercado) se cobra al bucket de cancelaciones en lugar del bucket de órdenes, así que una clave que gastó toda su asignación de envíos enviando órdenes todavía tiene una asignación completa e intacta para retirarlas.

Esta es la única asimetría deliberada del modelo. Los envíos pueden esperar, y la reducción del riesgo no. Un limitador que rechaza tu cancelación mientras una posición se mueve en tu contra convierte un control de uso justo en una pérdida. Por eso la regla es incondicional. Agotar los envíos nunca puede rechazar una cancelación, porque los dos nunca consumen el mismo token.

Para un cliente, esto significa:

  • Nunca deduzcas tu holgura de cancelaciones a partir de un 429 de envío. Un bucket order vacío no dice nada sobre el cancel. Un cliente que aplica backoff a todas las llamadas de órdenes después de un rechazo de envío frenó justo la llamada que todavía debería estar haciendo.

  • Un bucket separado no es una forma de saltarse el límite. Las cancelaciones se miden a la misma tasa por segundo del nivel que todo lo demás, así que un ciclo de cancelaciones igual puede recibir un 429. Lleva bucket: cancel, el único rechazo que significa que tu canal de cancelaciones está saturado. En ese caso, respeta retry-after.

Las modificaciones se cobran como envíos, no como cancelaciones. PATCH no indica si una modificación reduce o aumenta la exposición, y una modificación que aumenta el tamaño es un envío se mire como se mire. Así que la garantía de arriba solo se aplica al método que siempre reduce el riesgo. Si necesitas la garantía, cancela.

POST /orders/preview también es una acción de trading, algo fácil de pasar por alto. Es una escritura bajo /orders y cuesta una unidad de clase trading, igual que enviar una orden. Por eso, previsualizar antes de cada orden reduce a la mitad tu tasa efectiva de envío. Presupuesta dos cobros de clase trading por cada orden enviada de esa forma, u omite la previsualización cuando ya conoces el dimensionamiento.

Quien llama presentando una clave HMAC pasa por un bucket por clave y luego por el bucket por propietario de su nivel, y el tope efectivo es el que se alcance primero. GET /account/rate-limit informa ese mínimo, y consultarlo no tiene costo. Es la única operación que no consume tokens, así que regular tu ritmo no puede frenarte.

Si tu cuenta acaba de subir de nivel, ten en cuenta que el tope propio de una clave se registra cuando se crea la clave (el valor predeterminado está en la tabla de niveles más abajo), y un cambio de nivel no lo reescribe. Por eso, una cuenta Market Maker que todavía usa una clave generada con el valor predeterminado puede quedar limitada al número de la clave y no al del nivel. Lee /account/rate-limit después de un cambio de nivel en lugar de suponer la cifra del nivel, y genera una clave nueva si el mínimo informado no es el que esperas.

Cómo leer los encabezados

  • En todas las respuestas autenticadas: x-ratelimit-limit y x-ratelimit-remaining.

  • Solo en un 429, además: x-ratelimit-reset (segundos Unix) y retry-after (segundos, nunca menos de 1).

No esperes los dos últimos en una respuesta exitosa. Un cliente que lee x-ratelimit-reset de una 2xx no lee nada.

remaining y retry-after usan unidades distintas, a propósito. remaining cuenta solicitudes de costo unitario, así que x-ratelimit-remaining: 10 significa diez solicitudes de peso 1 o dos pesadas. retry-after se deriva del costo ponderado de la solicitud rechazada. Un limitador que lee remaining como "solicitudes del tipo que estoy por enviar" enviará de más en los endpoints pesados y provocará sus propios 429.

Un rechazo es un HTTP 429 con este cuerpo:

Ramifica según code y luego según bucket. Es uno de key, owner, order, cancel, ip o login, y para los cuatro primeros es la clave que hay que buscar en buckets de GET /account/rate-limit. ip y login nunca aparecen allí. Ambos miden una conexión y no una cuenta, y login se rechaza incluso antes de que exista una cuenta. El mismo valor se envía en el encabezado x-ratelimit-bucket, para que un proxy o un wrapper de reintentos pueda leerlo sin analizar el cuerpo. El message nombra qué bucket se agotó (Rate limit exceeded, API key rate limit exceeded, Order placement rate limit exceeded o IP rate limit exceeded). Es un diagnóstico. Su redacción no es estable, así que no lo compares de forma programática.

A diferencia de los rechazos 403 por jurisdicción, un 429 sí se puede reintentar: respeta retry-after y aplica backoff. Regular tu ritmo según x-ratelimit-remaining es mejor que descubrir el tope chocando con él.

El GET /account/rate-limit sin costo informa el mismo estado sin gastar un token. Los campos de nivel superior cubren la clase de solicitudes, y buckets cubre cada presupuesto:

buckets se indexa con las mismas etiquetas que usa un 429, tanto en su campo bucket como en el encabezado x-ratelimit-bucket. Así, un rechazo corresponde directamente al estado que lo causó: buckets[error.bucket]. Construye tu limitador en torno a esa búsqueda. key solo aparece cuando te limita un bucket por clave, y ni ip ni login aparecen nunca, porque ambos miden una conexión y no una cuenta.

Los limit, remaining y reset_at_ms de nivel superior son la clase de solicitudes, sin cambios, así que nada de lo que los lee necesita cambiar. No responden "¿puedo enviar una orden?". Eso lo responde buckets.order. En una clave que acaba de cotizar hasta llegar al tope de envíos, los dos no coinciden en absoluto: remaining: 20 junto con buckets.order.remaining: 0.

Dos detalles que hay que manejar cuando usas tanto este endpoint como los encabezados. reset_at_ms está en milisegundos, mientras que el encabezado x-ratelimit-reset está en segundos Unix. Y el nombre del nivel está en minúsculas aquí (pro) pero no en el cuerpo del 429 (Pro), así que compáralo sin distinguir mayúsculas de minúsculas en lugar de contra un literal.

Los tres campos numéricos son null para quien llama con nivel Unlimited, que se agrupa por IP y no por cuenta. buckets vuelve como {}, porque ningún presupuesto de alcance de cuenta mide a quien llama con ese nivel.

Niveles y topes actuales

Los niveles son multiplicadores sobre un mismo modelo, no modelos distintos. Pro es el predeterminado para todas las cuentas. MarketMaker lo asigna un administrador. Solicítalo a través de tu contacto en Nexus, y consulta la Guía para market makers. Unlimited existe para claves de gateway que multiplexan a muchos usuarios y nunca se asigna a una cuenta de trading.

Nivel
Solicitudes
Acciones de trading
Conexiones WS
Suscripciones WS
Frames entrantes WS

Pro

20/s

20/s

5

50

10/s

MarketMaker

2,000/s

2,000/s

100

1,000

50/s

Unlimited

por IP, 50/s

por IP, 50/s, el mismo bucket que las lecturas

exento

exento

exento

Por IP de cliente, en todos los niveles
Tope

Lecturas de datos de mercado sin autenticación

50/s

POST /auth/login, en un bucket propio

5/s

Conexiones WebSocket, verificadas en el upgrade

5

Una clave de API nueva se crea con su propio tope de 20/s.

Los topes por IP se aplican sin importar el nivel, todos antes de que tengas una credencial. POST /auth/login se mide de forma mucho más estricta que las lecturas, en un bucket propio. Un intento de inicio de sesión cuesta un ecrecover sobre una firma EIP-191, y cualquiera que pueda llegar al endpoint puede hacer que el servidor pague ese costo. El límite no es una defensa contra adivinar el secreto, ya que el mensaje firmado es una constante fija y no hay nada que probar por fuerza bruta. Es una defensa de costos. El tráfico cuya IP de cliente no se puede resolver en absoluto tampoco se admite sin límite. Las lecturas comparten un bucket y los inicios de sesión comparten un segundo bucket, separado, así que una avalancha que elimina su x-forwarded-for no puede agotar el bucket del que depende el resto del tráfico que no se puede resolver. Un origen que no se puede resolver recibe un tope compartido, no la ausencia de tope. Su tamaño se define en cada despliegue, por eso no aparece en la tabla de arriba.

Un rechazo de inicio de sesión lleva bucket: login, que es el único rechazo que significa que la propia validación de inicio de sesión está saturada. Es un tope por IP, así que lo compartes con todos los que están detrás de tu NAT o de tu proxy de salida, y no dice nada sobre tu cuenta. Todavía no hay cuenta.

Las cancelaciones tienen su propio presupuesto a la tasa de acciones de trading. La tabla no les da una columna propia porque los dos números son iguales por diseño.

Unlimited tiene exenciones más acotadas de lo que sugiere su nombre. Sus escrituras de órdenes y sus cancelaciones no están exentas de los límites de solicitudes. Se saltan tanto el bucket de trading dedicado como el bucket de cancelaciones, y se cobran al mismo bucket por IP que sus lecturas. Así que allí no se cumplen ni la independencia de clases de arriba ni la garantía de cancelación, y el propio polling de un gateway puede desplazar su flujo de órdenes, en el nivel cuya mezcla de tráfico es la menos predecible. Los topes de WS de los que está exento son los por cuenta. El tope de conexiones por IP de abajo sigue aplicándose.

Estos números son los valores predeterminados actuales, no un contrato fijo. Se generan a partir de la extensión x-nexus-rate-limits del contrato, que una prueba mantiene igual a lo que aplica el servicio, y cambiarán a medida que se finalice el sistema de niveles. Lee /account/rate-limit en lugar de fijarlos en el código.

Topes de WebSocket

Los topes de WebSocket son una clase de recursos propia, independiente del presupuesto de solicitudes REST y del presupuesto de acciones de trading. Agotar uno no afecta a los demás.

Las conexiones tienen un tope por cuenta según el nivel (la columna Conexiones WS de arriba). Un tope por IP separado (la tabla por IP) se aplica primero, en el momento del upgrade, y rige en todos los niveles, incluido Unlimited, así que una sola dirección de origen no puede alcanzar por sí sola la cifra por cuenta. Una conexión rechazada ahí recibe un HTTP 429 (ws_conn_limit_exceeded) en la solicitud de upgrade en lugar de un frame de cierre. El token de stream de un solo uso se consume de todos modos, así que genera uno nuevo antes de reconectarte.

Las suscripciones tienen el mismo tope dos veces: por conexión y entre todas las conexiones de una cuenta. Por eso, abrir más sockets no da más suscripciones. Volver a suscribirte a una clave (channel, market) que ya tienes la reemplaza en el lugar y no tiene costo. Superar el tope devuelve un frame error (subscription_limit_exceeded) en lugar de un código de estado, porque no hay respuestas HTTP una vez que el socket está abierto.

Los frames entrantes (tus suscripciones, desuscripciones y pings) están limitados a la tasa sostenida por nivel, con una ráfaga de 2× tolerada por encima, así que una tormenta de reconexiones y resuscripciones no se penaliza. Más allá de eso, el servidor descarta los frames que superan el límite y envía un aviso error por ventana de contabilización en lugar de uno por frame, para que una avalancha entrante no se convierta en una saliente. Una inundación sostenida, es decir, suficientes frames descartados dentro de una ventana, cierra la conexión con el código 1008 (violación de política). El servidor no aplica un frame descartado y no te avisa. Si no ves un ack subscribed, vuelve a enviar la suscripción después de un backoff en lugar de suponer que se aplicó.

Los presupuestos son por red

Cada red es un despliegue propio, así que cada red tiene sus propios buckets. Gastar en testnet no reduce la holgura de mainnet cuando se lance mainnet, ni al revés. Las credenciales tampoco cruzan de una red a otra. Una clave queda vinculada a la red que la generó. Consulta Redes.

Dónde se aplican los límites hoy

Hay una limitación actual que se ve desde afuera, y funciona a tu favor, no en tu contra.

El proceso del gateway mantiene el estado del limitador en memoria, no en un almacén compartido. De esto se derivan dos cosas. Primero, no es duradero. Un nuevo despliegue reinicia tus buckets, y una subida de nivel puede volver brevemente al nivel base hasta que se vuelva a aplicar. Segundo, cuando el gateway de una red ejecuta más de una réplica, cada una mantiene sus propios buckets, así que el tope agregado que observa un cliente puede ser más alto que la cifra por segundo publicada, según cómo se distribuyan sus conexiones.

No diseñes contando con esa holgura. Trata el número publicado como el tope al que tienes derecho y regula tu ritmo según él. El extra proviene de dónde se aplican actualmente los límites, no se distribuye de forma pareja y desaparecerá cuando los contadores pasen a un almacén compartido. Un cliente construido según la cifra publicada seguirá funcionando cuando eso ocurra. Uno ajustado al agregado observado empezará a ver 429.

¿Tu firmador puede seguir el ritmo?

El cliente firma cada solicitud autenticada antes de que salga, así que el firmador impone un tope propio por debajo de los presupuestos de arriba. Revisa ese tope antes de elegir un SDK. Depende mucho más del esquema y de la biblioteca criptográfica que del lenguaje.

El Exchange acepta dos esquemas de firma de solicitudes (consulta Autenticación y Agentes):

  • HMAC (hmacAuth). HMAC-SHA256 sobre la cadena canónica de cinco campos, con el secreto de la API decodificado de hex como clave. Un MAC simétrico toma microsegundos en cualquier lenguaje.

  • Clave de agente (agentAuth). ECDSA secp256k1 sobre el keccak256 de la cadena canónica de seis campos, con S bajo, enviada como r||s||v de 65 bytes. Es una firma de curva elíptica, así que su costo depende de si la biblioteca subyacente es código nativo o código interpretado puro.

Medido a través de la ruta de firma propia de cada SDK, sobre un cuerpo fijo de POST /api/v1/orders, en un solo hilo. Es la misma llamada que hace el cliente en cada solicitud, incluida la construcción de la cadena canónica, el hash del cuerpo y la codificación en hex del resultado, pero sin E/S de red:

SDK
Esquema
Criptografía subyacente
p50
p95
Firmas/s
p50 en 5 corridas

Rust (nexus-exchange-rs)

HMAC

hmac + sha2

0.9 µs

0.9 µs

~1,080,000

0.88–0.96 µs

Rust (nexus-exchange-rs)

Clave de agente

k256

79 µs

91 µs

~12,500

74–79 µs

TypeScript (nexus-exchange-ts)

HMAC

Web Crypto (asíncrono)

27 µs

43 µs

~29,000

24–35 µs

TypeScript (nexus-exchange-ts)

Clave de agente

@noble/curves

319 µs

470 µs

~2,900

293–645 µs

Python (nexus-exchange-py)

HMAC

hmac de stdlib

2.1 µs

2.2 µs

~460,000

1.96–2.25 µs

Python, instalación predeterminada

Clave de agente

backend Python puro de eth-keys

3.5 ms

4.2 ms

~280

3.43–4.11 ms

Python + coincurve

Clave de agente

libsecp256k1 a través de coincurve

97 µs

110 µs

~10,000

96–116 µs

La CLI firma a través del crate de Rust, así que hereda las filas de Rust en lugar de tener cifras propias.

En el nivel Pro, ningún SDK ni ningún esquema está limitado por el firmador. 20 solicitudes por segundo dejan 50 ms por firma. La fila más lenta, la firma con clave de agente en una instalación predeterminada de Python, usa 3.5 ms de eso, así que puede firmar unas catorce veces la tasa Pro en un solo núcleo. Incluso quien llama saturando los tres buckets Pro a la vez (solicitudes, acciones de trading y cancelaciones, 60 solicitudes firmadas por segundo) gasta alrededor de una quinta parte de un núcleo en firmar. El firmador con clave de agente de TypeScript tiene más de cien veces esa holgura, y HMAC es prácticamente gratis en todos los SDK.

En el nivel MarketMaker, el esquema y la biblioteca empiezan a importar. Sus 2,000 acciones de trading por segundo, más otras 2,000 cancelaciones aparte, dejan 0.25–0.5 ms por firma en un solo hilo.

  • Python con clave de agente: una instalación predeterminada firma unas 280 por segundo, aproximadamente una séptima parte del tope de trading. Instala coincurve (pip install coincurve) en el mismo entorno y AgentSigner pasa a funcionar sobre libsecp256k1, a unas 10,000 por segundo, sin ningún cambio en tu código. El SDK no elige un backend de curva por sí mismo; eth-keys usa coincurve siempre que puede importarlo, salvo que la variable de entorno ECC_BACKEND_CLASS nombre otro backend. El benchmark de Python muestra el backend que midió, que es la forma más rápida de confirmar que el cambio funcionó. O firma con HMAC, que nunca es la restricción.

  • TypeScript con clave de agente: unas 2,900 por segundo alcanzan para el tope de trading en un solo núcleo, pero no para trading y cancelaciones juntos. Un cliente que ejecuta ambos a tasa completa debería firmar con HMAC o repartir la firma entre worker threads.

  • Rust y la CLI: holgados con cualquiera de los dos esquemas.

Por solicitud, la firma agrega 0.3 ms en TypeScript y 3.5 ms en una instalación predeterminada de Python con clave de agente. Es una fracción pequeña de un viaje de ida y vuelta del cliente que se mide en decenas de milisegundos, pero se nota en la ruta de Python. Es otra razón para instalar coincurve si operas desde Python con una clave de agente.

Cómo se comparan con las cifras publicadas por Paradex

Paradex publica una tabla similar: unas 5,000 firmas por segundo en Rust, 1,430 en Go, 50 en TypeScript y 8 en Python o Java. Tomados al pie de la letra, los 20 ms por firma de TypeScript limitan un hilo a 50 firmas por segundo. Eso cubre un solo presupuesto de 20/s, pero la firma sola usaría el 40% del núcleo, y no alcanza para los 60/s que puede gastar quien llama con nivel Pro entre solicitudes, acciones de trading y cancelaciones juntas. Esas cifras no se trasladan. Paradex firma las órdenes con una clave de StarkNet, una curva distinta y un hash distinto de cualquiera de los dos esquemas de aquí, y sus números no se midieron en el hardware de abajo, así que una proporción entre las dos tablas es, como mucho, orientativa. El patrón sí se traslada. Un firmador de curva elíptica en código interpretado puro es el caso lento, y los bindings nativos cierran la mayor parte de la brecha. La diferencia aquí es que el caso lento (Python con su alternativa en Python puro, unas 280 por segundo) sigue siendo más de diez veces más rápido de lo que necesita el nivel Pro.

Metodología

  • Hardware. Apple M2, 8 núcleos (4 de rendimiento, 4 de eficiencia), 8 GB, macOS 26.5 (build 25F84), conectado a la corriente y con el Modo de bajo consumo desactivado. macOS no puede fijar un proceso a un núcleo, y la máquina no estaba inactiva en otros aspectos (carga promedio de 4–14 durante las corridas), así que lee la columna p95 y el rango como límites superiores.

  • Runtimes. Rust 1.97 (perfil release), Node.js 22.23, Python 3.14 para las filas de HMAC y de clave de agente predeterminada. La fila de coincurve corre en Python 3.13, porque coincurve todavía no publica un wheel para Python 3.14. La fila de clave de agente predeterminada mide lo mismo en 3.13 (3.5 ms), así que el intérprete no explica la diferencia.

  • Fixture. Idéntico en los tres SDK: POST, ruta /api/v1/orders, consulta vacía, un cuerpo de orden límite de 155 bytes y una marca de tiempo fija. El firmador de agente emite un nonce nuevo en cada iteración, como lo hace en una escritura real. Ambos esquemas producen firmas idénticas byte a byte entre los SDK para este fixture.

  • Procedimiento. Dos segundos de calentamiento, luego cada firma se cronometra por separado (Rust 20,000, TypeScript 5,000, Python 3,000 o 5,000) y se toman p50 y p95 de las muestras. Las firmas por segundo son la cantidad de muestras dividida por el tiempo real del ciclo cronometrado. Cada benchmark se ejecutó cinco veces, intercalado con los demás. La tabla muestra la corrida mediana y el rango del p50 en las cinco. El benchmark de Rust también ejecuta criterion, cuya estimación de la media coincidió con el ciclo cronometrado (76–85 µs para la clave de agente).

  • Cómo volver a ejecutarlo. Cada SDK incluye su benchmark: cargo bench --bench signing en nexus-exchange-rs, pnpm bench en nexus-exchange-ts y python bench/signing_bench.py en nexus-exchange-py. El benchmark de Python muestra qué backend de eth-keys midió.

Cómo diseñar según los límites

  • Agrupa en lotes en lugar de hacer ciclos. POST /orders/batch cobra 1 + floor(n / 40), así que 40 órdenes en una solicitud cuestan una cuadragésima parte de 40 envíos individuales.

  • Usa streams en lugar de polling. Los datos del libro de órdenes y de las operaciones por WebSocket no cuestan nada de tu presupuesto de solicitudes, y llegan antes: el frame de suscripción es un frame entrante, y el stream que sigue es gratis.

  • Presupuesta las lecturas pesadas por su costo real. /fills, /orders/history, /account/summary y /account/portfolio-history cuestan 5 cada una. Consultar las cuatro cada segundo cuesta 20/s, que es todo el presupuesto de quien llama con nivel Pro.

  • Prefiere una sola lectura coherente. GET /account/state devuelve juntos el resumen y todas las posiciones abiertas. Es más barato que dos llamadas y no tiene la condición de carrera entre ellas (consulta Portafolio y estado de la cuenta).

  • Guarda en caché lo que no cambia. Los metadatos de mercado de GET /markets no necesitan volver a obtenerse en cada ciclo.

  • Regula tu ritmo según los encabezados y según /account/rate-limit. Consultar ese endpoint no tiene costo. Reintentar a ciegas después de un 429, sí.

Relacionado

Estado: versión preliminar de desarrollo en testnet. Mainnet no se ha lanzado. Cada tope de esta página es configuración actual y no un contrato congelado. Lee /account/rate-limit en producción en lugar de fijar un número en el código, y fija una versión publicada de la especificación para que los pesos por operación sobre los que construyes no cambien. El estado del limitador todavía no persiste entre reinicios del gateway. Las credenciales y los saldos de testnet no tienen valor en el mundo real.

Última actualización