API clients
Official TypeScript and Python clients are published for the Tenor API. The clients wrap the same endpoints as the API reference and handle the API key, retries, errors and paging.
Both clients are generated from the API's OpenAPI spec, so every endpoint is covered and every request and response is typed. The source is maintained in tenor-labs/tenor-api-clients.
| Language | Package | Requirements | Source |
|---|---|---|---|
| TypeScript | @tenor-labs/api-client | Node 20.3+, or any runtime with fetch and AbortSignal.any. No dependencies. | packages/typescript |
| Python | tenor-api-client | Python 3.11+. Sync and async clients. | packages/python |
Installation
- TypeScript
- Python
npm install @tenor-labs/api-client
pip install tenor-api-client
Quickstart
The example below lists the markets on Base, takes the first maturity of the first market, and requests a quote to lend 1 USDC.
- TypeScript
- Python
import { Tenor } from "@tenor-labs/api-client";
const tenor = new Tenor({ apiKey: process.env.TENOR_API_KEY });
const { data: markets } = await tenor.listMarkets({ chainId: 8453 });
const { data: maturities } = await tenor.listMarketMaturities({
marketId: markets[0].marketId,
chainId: 8453,
});
const { data: quote } = await tenor.getQuote({
chainId: 8453,
maturityId: maturities[0].maturityId,
action: "lend",
size: { principal: "1000000" }, // 1 USDC, in the token's smallest unit
});
from tenor_api_client import Tenor
with Tenor() as tenor: # the key is read from $TENOR_API_KEY
market = tenor.list_markets(chain_id=8453).data[0]
maturity = tenor.list_market_maturities(market.market_id, chain_id=8453).data[0]
quote = tenor.get_quote(body={
"chain_id": 8453,
"maturity_id": maturity.maturity_id,
"action": "lend",
"size": {"principal": "1000000"}, # 1 USDC, in the token's smallest unit
})
print(quote.data.rate)
An async client, AsyncTenor, exposes the same methods for use with async with and await.
Conventions
- Each API operation is exposed as one method, named after the operation:
getQuote/get_quote,prepareLend/prepare_lend,listPositions/list_positions, and so on. - Every method returns
dataandmeta.metaholds the request ID and, on paged lists, the next cursor. - Amounts and rates are returned as strings, as on the wire. Amounts are expressed in the token's smallest unit.
- In the TypeScript client, fields are renamed to camelCase (
maturityIdformaturity_id); enum values are left unchanged. In the Python client, the API's snake_case names are kept.
API keys
Reads and quotes are served without an API key. Prepares, simulations and readiness checks require one. When no key is passed explicitly, both clients read it from the TENOR_API_KEY environment variable and send it in the X-Api-Key header.
Keys starting with tnr_sk_test_ are test keys: every prepare made with a test key is a dry run.
Keys should be kept server-side and never shipped to a browser or a mobile app. Scopes and key requests are covered in Authentication.
Configuration
Both clients retry 429 responses (after the Retry-After delay), 502, 503 and 504 responses, and network errors.
- TypeScript
- Python
const tenor = new Tenor({
apiKey: "tnr_sk_live_…", // defaults to process.env.TENOR_API_KEY
timeout: 30_000, // per attempt, in ms
maxRetries: 2,
headers: { "X-Request-Id": "my-trace-id" },
});
// Per-call options
await tenor.listChains({}, { signal: AbortSignal.timeout(5_000) });
tenor = Tenor(
api_key="tnr_sk_live_…", # defaults to $TENOR_API_KEY
timeout=30.0, # per attempt, in seconds
max_retries=2,
headers={"X-Request-Id": "my-trace-id"},
)
Errors
API errors are raised as TenorApiError. Integrations should branch on code, with treatment as the fallback for codes that are not handled explicitly. Network failures and timeouts are raised as TenorConnectionError. Error codes and treatments are listed in Errors.
| Field | Description |
|---|---|
status | HTTP status, for example 422 |
code | Machine-readable error code, for example INSUFFICIENT_LIQUIDITY |
treatment | Recommended next step: fix_request, refetch_state, fund_account, or retry |
details | Additional context, as sent by the API |
requestId / request_id | Request identifier, to be included in support requests |
- TypeScript
- Python
import { TenorApiError } from "@tenor-labs/api-client";
try {
await tenor.prepareLend({ /* … */ });
} catch (err) {
if (err instanceof TenorApiError) {
console.error(err.code, err.treatment, err.requestId);
}
}
from tenor_api_client import TenorApiError
try:
tenor.prepare_lend(body={...})
except TenorApiError as e:
print(e.code, e.treatment, e.request_id)
Pagination
Offers, positions, and fills are paged. The cursor for the next page is returned in meta and is empty on the last page.
- TypeScript
- Python
let cursor: string | undefined;
do {
const page = await tenor.listPositions({ wallet, chainId: 8453, cursor });
// page.data holds this page's positions
cursor = page.meta.nextCursor ?? undefined;
} while (cursor);
cursor = None
while True:
page = tenor.list_positions(wallet, chain_id=8453, cursor=cursor)
# page.data holds this page's positions
cursor = page.meta.next_cursor
if not cursor:
break
Further reading
- API reference: every endpoint, with request and response schemas.
- TypeScript client README and Python client README: full documentation for each client.