Skip to main content

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.

LanguagePackageRequirementsSource
TypeScript@tenor-labs/api-clientNode 20.3+, or any runtime with fetch and AbortSignal.any. No dependencies.packages/typescript
Pythontenor-api-clientPython 3.11+. Sync and async clients.packages/python

Installation​

npm install @tenor-labs/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.

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
});

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 data and meta. meta holds 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 (maturityId for maturity_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.

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) });

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.

FieldDescription
statusHTTP status, for example 422
codeMachine-readable error code, for example INSUFFICIENT_LIQUIDITY
treatmentRecommended next step: fix_request, refetch_state, fund_account, or retry
detailsAdditional context, as sent by the API
requestId / request_idRequest identifier, to be included in support requests
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);
}
}

Pagination​

Offers, positions, and fills are paged. The cursor for the next page is returned in meta and is empty on the last page.

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);

Further reading​