client.ts•32.7 kB
// File generated from our OpenAPI spec by Stainless. See CONTRIBUTING.md for details.
import type { RequestInit, RequestInfo, BodyInit } from './internal/builtin-types';
import type { HTTPMethod, PromiseOrValue, MergedRequestInit, FinalizedRequestInit } from './internal/types';
import { uuid4 } from './internal/utils/uuid';
import { validatePositiveInteger, isAbsoluteURL, safeJSON } from './internal/utils/values';
import { sleep } from './internal/utils/sleep';
export type { Logger, LogLevel } from './internal/utils/log';
import { castToError, isAbortError } from './internal/errors';
import type { APIResponseProps } from './internal/parse';
import { getPlatformHeaders } from './internal/detect-platform';
import * as Shims from './internal/shims';
import * as Opts from './internal/request-options';
import { VERSION } from './version';
import * as Errors from './core/error';
import * as Uploads from './core/uploads';
import * as API from './resources/index';
import { APIPromise } from './core/api-promise';
import {
AssetPlatformGetParams,
AssetPlatformGetResponse,
AssetPlatforms,
} from './resources/asset-platforms';
import { Entities, EntityGetListParams, EntityGetListResponse } from './resources/entities';
import { ExchangeRateGetResponse, ExchangeRates } from './resources/exchange-rates';
import { Key, KeyGetResponse } from './resources/key';
import { Ping, PingGetResponse } from './resources/ping';
import {
PublicTreasury,
PublicTreasuryGetCoinIDParams,
PublicTreasuryGetCoinIDResponse,
PublicTreasuryGetEntityIDResponse,
} from './resources/public-treasury';
import { TokenListGetAllJsonResponse, TokenLists } from './resources/token-lists';
import { CoinGetIDParams, CoinGetIDResponse, Coins } from './resources/coins/coins';
import { DerivativeGetResponse, Derivatives } from './resources/derivatives/derivatives';
import {
ExchangeGetIDParams,
ExchangeGetIDResponse,
ExchangeGetListParams,
ExchangeGetListResponse,
ExchangeGetParams,
ExchangeGetResponse,
Exchanges,
} from './resources/exchanges/exchanges';
import { Global, GlobalGetResponse } from './resources/global/global';
import {
NFTGetIDResponse,
NFTGetListParams,
NFTGetListResponse,
NFTGetMarketsParams,
NFTGetMarketsResponse,
NFTs,
} from './resources/nfts/nfts';
import { Onchain } from './resources/onchain/onchain';
import { Search, SearchGetParams, SearchGetResponse } from './resources/search/search';
import { Simple } from './resources/simple/simple';
import { type Fetch } from './internal/builtin-types';
import { HeadersLike, NullableHeaders, buildHeaders } from './internal/headers';
import { FinalRequestOptions, RequestOptions } from './internal/request-options';
import { readEnv } from './internal/utils/env';
import {
type LogLevel,
type Logger,
formatRequestDetails,
loggerFor,
parseLogLevel,
} from './internal/utils/log';
import { isEmptyObj } from './internal/utils/values';
const environments = {
pro: 'https://pro-api.coingecko.com/api/v3',
demo: 'https://api.coingecko.com/api/v3',
};
type Environment = keyof typeof environments;
export interface ClientOptions {
/**
* CoinGecko Pro API Key
*/
proAPIKey?: string | null | undefined;
/**
* CoinGecko Demo API Key
*/
demoAPIKey?: string | null | undefined;
/**
* Specifies the environment to use for the API.
*
* Each environment maps to a different base URL:
* - `pro` corresponds to `https://pro-api.coingecko.com/api/v3`
* - `demo` corresponds to `https://api.coingecko.com/api/v3`
*/
environment?: Environment | undefined;
/**
* Override the default base URL for the API, e.g., "https://api.example.com/v2/"
*
* Defaults to process.env['COINGECKO_BASE_URL'].
*/
baseURL?: string | null | undefined;
/**
* The maximum amount of time (in milliseconds) that the client should wait for a response
* from the server before timing out a single request.
*
* Note that request timeouts are retried by default, so in a worst-case scenario you may wait
* much longer than this timeout before the promise succeeds or fails.
*
* @unit milliseconds
*/
timeout?: number | undefined;
/**
* Additional `RequestInit` options to be passed to `fetch` calls.
* Properties will be overridden by per-request `fetchOptions`.
*/
fetchOptions?: MergedRequestInit | undefined;
/**
* Specify a custom `fetch` function implementation.
*
* If not provided, we expect that `fetch` is defined globally.
*/
fetch?: Fetch | undefined;
/**
* The maximum number of times that the client will retry a request in case of a
* temporary failure, like a network error or a 5XX error from the server.
*
* @default 2
*/
maxRetries?: number | undefined;
/**
* Default headers to include with every request to the API.
*
* These can be removed in individual requests by explicitly setting the
* header to `null` in request options.
*/
defaultHeaders?: HeadersLike | undefined;
/**
* Default query parameters to include with every request to the API.
*
* These can be removed in individual requests by explicitly setting the
* param to `undefined` in request options.
*/
defaultQuery?: Record<string, string | undefined> | undefined;
/**
* Set the log level.
*
* Defaults to process.env['COINGECKO_LOG'] or 'warn' if it isn't set.
*/
logLevel?: LogLevel | undefined;
/**
* Set the logger.
*
* Defaults to globalThis.console.
*/
logger?: Logger | undefined;
}
/**
* API Client for interfacing with the Coingecko API.
*/
export class Coingecko {
proAPIKey: string | null;
demoAPIKey: string | null;
baseURL: string;
maxRetries: number;
timeout: number;
logger: Logger | undefined;
logLevel: LogLevel | undefined;
fetchOptions: MergedRequestInit | undefined;
private fetch: Fetch;
#encoder: Opts.RequestEncoder;
protected idempotencyHeader?: string;
private _options: ClientOptions;
/**
* API Client for interfacing with the Coingecko API.
*
* @param {string | null | undefined} [opts.proAPIKey=process.env['COINGECKO_PRO_API_KEY'] ?? null]
* @param {string | null | undefined} [opts.demoAPIKey=process.env['COINGECKO_DEMO_API_KEY'] ?? null]
* @param {Environment} [opts.environment=pro] - Specifies the environment URL to use for the API.
* @param {string} [opts.baseURL=process.env['COINGECKO_BASE_URL'] ?? https://pro-api.coingecko.com/api/v3] - Override the default base URL for the API.
* @param {number} [opts.timeout=1 minute] - The maximum amount of time (in milliseconds) the client will wait for a response before timing out.
* @param {MergedRequestInit} [opts.fetchOptions] - Additional `RequestInit` options to be passed to `fetch` calls.
* @param {Fetch} [opts.fetch] - Specify a custom `fetch` function implementation.
* @param {number} [opts.maxRetries=2] - The maximum number of times the client will retry a request.
* @param {HeadersLike} opts.defaultHeaders - Default headers to include with every request to the API.
* @param {Record<string, string | undefined>} opts.defaultQuery - Default query parameters to include with every request to the API.
*/
constructor({
baseURL = readEnv('COINGECKO_BASE_URL'),
proAPIKey = readEnv('COINGECKO_PRO_API_KEY') ?? null,
demoAPIKey = readEnv('COINGECKO_DEMO_API_KEY') ?? null,
...opts
}: ClientOptions = {}) {
const options: ClientOptions = {
proAPIKey,
demoAPIKey,
...opts,
baseURL,
environment: opts.environment ?? 'pro',
};
if (baseURL && opts.environment) {
throw new Errors.CoingeckoError(
'Ambiguous URL; The `baseURL` option (or COINGECKO_BASE_URL env var) and the `environment` option are given. If you want to use the environment you must pass baseURL: null',
);
}
this.baseURL = options.baseURL || environments[options.environment || 'pro'];
this.timeout = options.timeout ?? Coingecko.DEFAULT_TIMEOUT /* 1 minute */;
this.logger = options.logger ?? console;
const defaultLogLevel = 'warn';
// Set default logLevel early so that we can log a warning in parseLogLevel.
this.logLevel = defaultLogLevel;
this.logLevel =
parseLogLevel(options.logLevel, 'ClientOptions.logLevel', this) ??
parseLogLevel(readEnv('COINGECKO_LOG'), "process.env['COINGECKO_LOG']", this) ??
defaultLogLevel;
this.fetchOptions = options.fetchOptions;
this.maxRetries = options.maxRetries ?? 2;
this.fetch = options.fetch ?? Shims.getDefaultFetch();
this.#encoder = Opts.FallbackEncoder;
this._options = options;
this.proAPIKey = proAPIKey;
this.demoAPIKey = demoAPIKey;
}
/**
* Create a new client instance re-using the same options given to the current client with optional overriding.
*/
withOptions(options: Partial<ClientOptions>): this {
const client = new (this.constructor as any as new (props: ClientOptions) => typeof this)({
...this._options,
environment: options.environment ? options.environment : undefined,
baseURL: options.environment ? undefined : this.baseURL,
maxRetries: this.maxRetries,
timeout: this.timeout,
logger: this.logger,
logLevel: this.logLevel,
fetch: this.fetch,
fetchOptions: this.fetchOptions,
proAPIKey: this.proAPIKey,
demoAPIKey: this.demoAPIKey,
...options,
});
return client;
}
/**
* Check whether the base URL is set to its default.
*/
#baseURLOverridden(): boolean {
return this.baseURL !== environments[this._options.environment || 'pro'];
}
protected defaultQuery(): Record<string, string | undefined> | undefined {
return this._options.defaultQuery;
}
protected validateHeaders({ values, nulls }: NullableHeaders) {
if (this.proAPIKey && values.get('x-cg-pro-api-key')) {
return;
}
if (nulls.has('x-cg-pro-api-key')) {
return;
}
if (this.demoAPIKey && values.get('x-cg-demo-api-key')) {
return;
}
if (nulls.has('x-cg-demo-api-key')) {
return;
}
throw new Error(
'Could not resolve authentication method. Expected either proAPIKey or demoAPIKey to be set. Or for one of the "x-cg-pro-api-key" or "x-cg-demo-api-key" headers to be explicitly omitted',
);
}
protected async authHeaders(opts: FinalRequestOptions): Promise<NullableHeaders | undefined> {
return buildHeaders([await this.proKeyAuth(opts), await this.demoKeyAuth(opts)]);
}
protected async proKeyAuth(opts: FinalRequestOptions): Promise<NullableHeaders | undefined> {
if (this.proAPIKey == null) {
return undefined;
}
return buildHeaders([{ 'x-cg-pro-api-key': this.proAPIKey }]);
}
protected async demoKeyAuth(opts: FinalRequestOptions): Promise<NullableHeaders | undefined> {
if (this.demoAPIKey == null) {
return undefined;
}
return buildHeaders([{ 'x-cg-demo-api-key': this.demoAPIKey }]);
}
/**
* Basic re-implementation of `qs.stringify` for primitive types.
*/
protected stringifyQuery(query: Record<string, unknown>): string {
return Object.entries(query)
.filter(([_, value]) => typeof value !== 'undefined')
.map(([key, value]) => {
if (typeof value === 'string' || typeof value === 'number' || typeof value === 'boolean') {
return `${encodeURIComponent(key)}=${encodeURIComponent(value)}`;
}
if (value === null) {
return `${encodeURIComponent(key)}=`;
}
throw new Errors.CoingeckoError(
`Cannot stringify type ${typeof value}; Expected string, number, boolean, or null. If you need to pass nested query parameters, you can manually encode them, e.g. { query: { 'foo[key1]': value1, 'foo[key2]': value2 } }, and please open a GitHub issue requesting better support for your use case.`,
);
})
.join('&');
}
private getUserAgent(): string {
return `${this.constructor.name}/JS ${VERSION}`;
}
protected defaultIdempotencyKey(): string {
return `stainless-node-retry-${uuid4()}`;
}
protected makeStatusError(
status: number,
error: Object,
message: string | undefined,
headers: Headers,
): Errors.APIError {
return Errors.APIError.generate(status, error, message, headers);
}
buildURL(
path: string,
query: Record<string, unknown> | null | undefined,
defaultBaseURL?: string | undefined,
): string {
const baseURL = (!this.#baseURLOverridden() && defaultBaseURL) || this.baseURL;
const url =
isAbsoluteURL(path) ?
new URL(path)
: new URL(baseURL + (baseURL.endsWith('/') && path.startsWith('/') ? path.slice(1) : path));
const defaultQuery = this.defaultQuery();
if (!isEmptyObj(defaultQuery)) {
query = { ...defaultQuery, ...query };
}
if (typeof query === 'object' && query && !Array.isArray(query)) {
url.search = this.stringifyQuery(query as Record<string, unknown>);
}
return url.toString();
}
/**
* Used as a callback for mutating the given `FinalRequestOptions` object.
*/
protected async prepareOptions(options: FinalRequestOptions): Promise<void> {}
/**
* Used as a callback for mutating the given `RequestInit` object.
*
* This is useful for cases where you want to add certain headers based off of
* the request properties, e.g. `method` or `url`.
*/
protected async prepareRequest(
request: RequestInit,
{ url, options }: { url: string; options: FinalRequestOptions },
): Promise<void> {}
get<Rsp>(path: string, opts?: PromiseOrValue<RequestOptions>): APIPromise<Rsp> {
return this.methodRequest('get', path, opts);
}
post<Rsp>(path: string, opts?: PromiseOrValue<RequestOptions>): APIPromise<Rsp> {
return this.methodRequest('post', path, opts);
}
patch<Rsp>(path: string, opts?: PromiseOrValue<RequestOptions>): APIPromise<Rsp> {
return this.methodRequest('patch', path, opts);
}
put<Rsp>(path: string, opts?: PromiseOrValue<RequestOptions>): APIPromise<Rsp> {
return this.methodRequest('put', path, opts);
}
delete<Rsp>(path: string, opts?: PromiseOrValue<RequestOptions>): APIPromise<Rsp> {
return this.methodRequest('delete', path, opts);
}
private methodRequest<Rsp>(
method: HTTPMethod,
path: string,
opts?: PromiseOrValue<RequestOptions>,
): APIPromise<Rsp> {
return this.request(
Promise.resolve(opts).then((opts) => {
return { method, path, ...opts };
}),
);
}
request<Rsp>(
options: PromiseOrValue<FinalRequestOptions>,
remainingRetries: number | null = null,
): APIPromise<Rsp> {
return new APIPromise(this, this.makeRequest(options, remainingRetries, undefined));
}
private async makeRequest(
optionsInput: PromiseOrValue<FinalRequestOptions>,
retriesRemaining: number | null,
retryOfRequestLogID: string | undefined,
): Promise<APIResponseProps> {
const options = await optionsInput;
const maxRetries = options.maxRetries ?? this.maxRetries;
if (retriesRemaining == null) {
retriesRemaining = maxRetries;
}
await this.prepareOptions(options);
const { req, url, timeout } = await this.buildRequest(options, {
retryCount: maxRetries - retriesRemaining,
});
await this.prepareRequest(req, { url, options });
/** Not an API request ID, just for correlating local log entries. */
const requestLogID = 'log_' + ((Math.random() * (1 << 24)) | 0).toString(16).padStart(6, '0');
const retryLogStr = retryOfRequestLogID === undefined ? '' : `, retryOf: ${retryOfRequestLogID}`;
const startTime = Date.now();
loggerFor(this).debug(
`[${requestLogID}] sending request`,
formatRequestDetails({
retryOfRequestLogID,
method: options.method,
url,
options,
headers: req.headers,
}),
);
if (options.signal?.aborted) {
throw new Errors.APIUserAbortError();
}
const controller = new AbortController();
const response = await this.fetchWithTimeout(url, req, timeout, controller).catch(castToError);
const headersTime = Date.now();
if (response instanceof globalThis.Error) {
const retryMessage = `retrying, ${retriesRemaining} attempts remaining`;
if (options.signal?.aborted) {
throw new Errors.APIUserAbortError();
}
// detect native connection timeout errors
// deno throws "TypeError: error sending request for url (https://example/): client error (Connect): tcp connect error: Operation timed out (os error 60): Operation timed out (os error 60)"
// undici throws "TypeError: fetch failed" with cause "ConnectTimeoutError: Connect Timeout Error (attempted address: example:443, timeout: 1ms)"
// others do not provide enough information to distinguish timeouts from other connection errors
const isTimeout =
isAbortError(response) ||
/timed? ?out/i.test(String(response) + ('cause' in response ? String(response.cause) : ''));
if (retriesRemaining) {
loggerFor(this).info(
`[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} - ${retryMessage}`,
);
loggerFor(this).debug(
`[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} (${retryMessage})`,
formatRequestDetails({
retryOfRequestLogID,
url,
durationMs: headersTime - startTime,
message: response.message,
}),
);
return this.retryRequest(options, retriesRemaining, retryOfRequestLogID ?? requestLogID);
}
loggerFor(this).info(
`[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} - error; no more retries left`,
);
loggerFor(this).debug(
`[${requestLogID}] connection ${isTimeout ? 'timed out' : 'failed'} (error; no more retries left)`,
formatRequestDetails({
retryOfRequestLogID,
url,
durationMs: headersTime - startTime,
message: response.message,
}),
);
if (isTimeout) {
throw new Errors.APIConnectionTimeoutError();
}
throw new Errors.APIConnectionError({ cause: response });
}
const responseInfo = `[${requestLogID}${retryLogStr}] ${req.method} ${url} ${
response.ok ? 'succeeded' : 'failed'
} with status ${response.status} in ${headersTime - startTime}ms`;
if (!response.ok) {
const shouldRetry = await this.shouldRetry(response);
if (retriesRemaining && shouldRetry) {
const retryMessage = `retrying, ${retriesRemaining} attempts remaining`;
// We don't need the body of this response.
await Shims.CancelReadableStream(response.body);
loggerFor(this).info(`${responseInfo} - ${retryMessage}`);
loggerFor(this).debug(
`[${requestLogID}] response error (${retryMessage})`,
formatRequestDetails({
retryOfRequestLogID,
url: response.url,
status: response.status,
headers: response.headers,
durationMs: headersTime - startTime,
}),
);
return this.retryRequest(
options,
retriesRemaining,
retryOfRequestLogID ?? requestLogID,
response.headers,
);
}
const retryMessage = shouldRetry ? `error; no more retries left` : `error; not retryable`;
loggerFor(this).info(`${responseInfo} - ${retryMessage}`);
const errText = await response.text().catch((err: any) => castToError(err).message);
const errJSON = safeJSON(errText);
const errMessage = errJSON ? undefined : errText;
loggerFor(this).debug(
`[${requestLogID}] response error (${retryMessage})`,
formatRequestDetails({
retryOfRequestLogID,
url: response.url,
status: response.status,
headers: response.headers,
message: errMessage,
durationMs: Date.now() - startTime,
}),
);
const err = this.makeStatusError(response.status, errJSON, errMessage, response.headers);
throw err;
}
loggerFor(this).info(responseInfo);
loggerFor(this).debug(
`[${requestLogID}] response start`,
formatRequestDetails({
retryOfRequestLogID,
url: response.url,
status: response.status,
headers: response.headers,
durationMs: headersTime - startTime,
}),
);
return { response, options, controller, requestLogID, retryOfRequestLogID, startTime };
}
async fetchWithTimeout(
url: RequestInfo,
init: RequestInit | undefined,
ms: number,
controller: AbortController,
): Promise<Response> {
const { signal, method, ...options } = init || {};
if (signal) signal.addEventListener('abort', () => controller.abort());
const timeout = setTimeout(() => controller.abort(), ms);
const isReadableBody =
((globalThis as any).ReadableStream && options.body instanceof (globalThis as any).ReadableStream) ||
(typeof options.body === 'object' && options.body !== null && Symbol.asyncIterator in options.body);
const fetchOptions: RequestInit = {
signal: controller.signal as any,
...(isReadableBody ? { duplex: 'half' } : {}),
method: 'GET',
...options,
};
if (method) {
// Custom methods like 'patch' need to be uppercased
// See https://github.com/nodejs/undici/issues/2294
fetchOptions.method = method.toUpperCase();
}
try {
// use undefined this binding; fetch errors if bound to something else in browser/cloudflare
return await this.fetch.call(undefined, url, fetchOptions);
} finally {
clearTimeout(timeout);
}
}
private async shouldRetry(response: Response): Promise<boolean> {
// Note this is not a standard header.
const shouldRetryHeader = response.headers.get('x-should-retry');
// If the server explicitly says whether or not to retry, obey.
if (shouldRetryHeader === 'true') return true;
if (shouldRetryHeader === 'false') return false;
// Retry on request timeouts.
if (response.status === 408) return true;
// Retry on lock timeouts.
if (response.status === 409) return true;
// Retry on rate limits.
if (response.status === 429) return true;
// Retry internal errors.
if (response.status >= 500) return true;
return false;
}
private async retryRequest(
options: FinalRequestOptions,
retriesRemaining: number,
requestLogID: string,
responseHeaders?: Headers | undefined,
): Promise<APIResponseProps> {
let timeoutMillis: number | undefined;
// Note the `retry-after-ms` header may not be standard, but is a good idea and we'd like proactive support for it.
const retryAfterMillisHeader = responseHeaders?.get('retry-after-ms');
if (retryAfterMillisHeader) {
const timeoutMs = parseFloat(retryAfterMillisHeader);
if (!Number.isNaN(timeoutMs)) {
timeoutMillis = timeoutMs;
}
}
// About the Retry-After header: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Retry-After
const retryAfterHeader = responseHeaders?.get('retry-after');
if (retryAfterHeader && !timeoutMillis) {
const timeoutSeconds = parseFloat(retryAfterHeader);
if (!Number.isNaN(timeoutSeconds)) {
timeoutMillis = timeoutSeconds * 1000;
} else {
timeoutMillis = Date.parse(retryAfterHeader) - Date.now();
}
}
// If the API asks us to wait a certain amount of time (and it's a reasonable amount),
// just do what it says, but otherwise calculate a default
if (!(timeoutMillis && 0 <= timeoutMillis && timeoutMillis < 60 * 1000)) {
const maxRetries = options.maxRetries ?? this.maxRetries;
timeoutMillis = this.calculateDefaultRetryTimeoutMillis(retriesRemaining, maxRetries);
}
await sleep(timeoutMillis);
return this.makeRequest(options, retriesRemaining - 1, requestLogID);
}
private calculateDefaultRetryTimeoutMillis(retriesRemaining: number, maxRetries: number): number {
const initialRetryDelay = 0.5;
const maxRetryDelay = 8.0;
const numRetries = maxRetries - retriesRemaining;
// Apply exponential backoff, but not more than the max.
const sleepSeconds = Math.min(initialRetryDelay * Math.pow(2, numRetries), maxRetryDelay);
// Apply some jitter, take up to at most 25 percent of the retry time.
const jitter = 1 - Math.random() * 0.25;
return sleepSeconds * jitter * 1000;
}
async buildRequest(
inputOptions: FinalRequestOptions,
{ retryCount = 0 }: { retryCount?: number } = {},
): Promise<{ req: FinalizedRequestInit; url: string; timeout: number }> {
const options = { ...inputOptions };
const { method, path, query, defaultBaseURL } = options;
const url = this.buildURL(path!, query as Record<string, unknown>, defaultBaseURL);
if ('timeout' in options) validatePositiveInteger('timeout', options.timeout);
options.timeout = options.timeout ?? this.timeout;
const { bodyHeaders, body } = this.buildBody({ options });
const reqHeaders = await this.buildHeaders({ options: inputOptions, method, bodyHeaders, retryCount });
const req: FinalizedRequestInit = {
method,
headers: reqHeaders,
...(options.signal && { signal: options.signal }),
...((globalThis as any).ReadableStream &&
body instanceof (globalThis as any).ReadableStream && { duplex: 'half' }),
...(body && { body }),
...((this.fetchOptions as any) ?? {}),
...((options.fetchOptions as any) ?? {}),
};
return { req, url, timeout: options.timeout };
}
private async buildHeaders({
options,
method,
bodyHeaders,
retryCount,
}: {
options: FinalRequestOptions;
method: HTTPMethod;
bodyHeaders: HeadersLike;
retryCount: number;
}): Promise<Headers> {
let idempotencyHeaders: HeadersLike = {};
if (this.idempotencyHeader && method !== 'get') {
if (!options.idempotencyKey) options.idempotencyKey = this.defaultIdempotencyKey();
idempotencyHeaders[this.idempotencyHeader] = options.idempotencyKey;
}
const headers = buildHeaders([
idempotencyHeaders,
{
Accept: 'application/json',
'User-Agent': this.getUserAgent(),
'X-Stainless-Retry-Count': String(retryCount),
...(options.timeout ? { 'X-Stainless-Timeout': String(Math.trunc(options.timeout / 1000)) } : {}),
...getPlatformHeaders(),
},
await this.authHeaders(options),
this._options.defaultHeaders,
bodyHeaders,
options.headers,
]);
this.validateHeaders(headers);
return headers.values;
}
private buildBody({ options: { body, headers: rawHeaders } }: { options: FinalRequestOptions }): {
bodyHeaders: HeadersLike;
body: BodyInit | undefined;
} {
if (!body) {
return { bodyHeaders: undefined, body: undefined };
}
const headers = buildHeaders([rawHeaders]);
if (
// Pass raw type verbatim
ArrayBuffer.isView(body) ||
body instanceof ArrayBuffer ||
body instanceof DataView ||
(typeof body === 'string' &&
// Preserve legacy string encoding behavior for now
headers.values.has('content-type')) ||
// `Blob` is superset of `File`
((globalThis as any).Blob && body instanceof (globalThis as any).Blob) ||
// `FormData` -> `multipart/form-data`
body instanceof FormData ||
// `URLSearchParams` -> `application/x-www-form-urlencoded`
body instanceof URLSearchParams ||
// Send chunked stream (each chunk has own `length`)
((globalThis as any).ReadableStream && body instanceof (globalThis as any).ReadableStream)
) {
return { bodyHeaders: undefined, body: body as BodyInit };
} else if (
typeof body === 'object' &&
(Symbol.asyncIterator in body ||
(Symbol.iterator in body && 'next' in body && typeof body.next === 'function'))
) {
return { bodyHeaders: undefined, body: Shims.ReadableStreamFrom(body as AsyncIterable<Uint8Array>) };
} else {
return this.#encoder({ body, headers });
}
}
static Coingecko = this;
static DEFAULT_TIMEOUT = 60000; // 1 minute
static CoingeckoError = Errors.CoingeckoError;
static APIError = Errors.APIError;
static APIConnectionError = Errors.APIConnectionError;
static APIConnectionTimeoutError = Errors.APIConnectionTimeoutError;
static APIUserAbortError = Errors.APIUserAbortError;
static NotFoundError = Errors.NotFoundError;
static ConflictError = Errors.ConflictError;
static RateLimitError = Errors.RateLimitError;
static BadRequestError = Errors.BadRequestError;
static AuthenticationError = Errors.AuthenticationError;
static InternalServerError = Errors.InternalServerError;
static PermissionDeniedError = Errors.PermissionDeniedError;
static UnprocessableEntityError = Errors.UnprocessableEntityError;
static toFile = Uploads.toFile;
assetPlatforms: API.AssetPlatforms = new API.AssetPlatforms(this);
coins: API.Coins = new API.Coins(this);
derivatives: API.Derivatives = new API.Derivatives(this);
entities: API.Entities = new API.Entities(this);
exchangeRates: API.ExchangeRates = new API.ExchangeRates(this);
exchanges: API.Exchanges = new API.Exchanges(this);
global: API.Global = new API.Global(this);
key: API.Key = new API.Key(this);
nfts: API.NFTs = new API.NFTs(this);
onchain: API.Onchain = new API.Onchain(this);
ping: API.Ping = new API.Ping(this);
publicTreasury: API.PublicTreasury = new API.PublicTreasury(this);
search: API.Search = new API.Search(this);
simple: API.Simple = new API.Simple(this);
tokenLists: API.TokenLists = new API.TokenLists(this);
}
Coingecko.AssetPlatforms = AssetPlatforms;
Coingecko.Coins = Coins;
Coingecko.Derivatives = Derivatives;
Coingecko.Entities = Entities;
Coingecko.ExchangeRates = ExchangeRates;
Coingecko.Exchanges = Exchanges;
Coingecko.Global = Global;
Coingecko.Key = Key;
Coingecko.NFTs = NFTs;
Coingecko.Onchain = Onchain;
Coingecko.Ping = Ping;
Coingecko.PublicTreasury = PublicTreasury;
Coingecko.Search = Search;
Coingecko.Simple = Simple;
Coingecko.TokenLists = TokenLists;
export declare namespace Coingecko {
export type RequestOptions = Opts.RequestOptions;
export {
AssetPlatforms as AssetPlatforms,
type AssetPlatformGetResponse as AssetPlatformGetResponse,
type AssetPlatformGetParams as AssetPlatformGetParams,
};
export {
Coins as Coins,
type CoinGetIDResponse as CoinGetIDResponse,
type CoinGetIDParams as CoinGetIDParams,
};
export { Derivatives as Derivatives, type DerivativeGetResponse as DerivativeGetResponse };
export {
Entities as Entities,
type EntityGetListResponse as EntityGetListResponse,
type EntityGetListParams as EntityGetListParams,
};
export { ExchangeRates as ExchangeRates, type ExchangeRateGetResponse as ExchangeRateGetResponse };
export {
Exchanges as Exchanges,
type ExchangeGetResponse as ExchangeGetResponse,
type ExchangeGetIDResponse as ExchangeGetIDResponse,
type ExchangeGetListResponse as ExchangeGetListResponse,
type ExchangeGetParams as ExchangeGetParams,
type ExchangeGetIDParams as ExchangeGetIDParams,
type ExchangeGetListParams as ExchangeGetListParams,
};
export { Global as Global, type GlobalGetResponse as GlobalGetResponse };
export { Key as Key, type KeyGetResponse as KeyGetResponse };
export {
NFTs as NFTs,
type NFTGetIDResponse as NFTGetIDResponse,
type NFTGetListResponse as NFTGetListResponse,
type NFTGetMarketsResponse as NFTGetMarketsResponse,
type NFTGetListParams as NFTGetListParams,
type NFTGetMarketsParams as NFTGetMarketsParams,
};
export { Onchain as Onchain };
export { Ping as Ping, type PingGetResponse as PingGetResponse };
export {
PublicTreasury as PublicTreasury,
type PublicTreasuryGetCoinIDResponse as PublicTreasuryGetCoinIDResponse,
type PublicTreasuryGetEntityIDResponse as PublicTreasuryGetEntityIDResponse,
type PublicTreasuryGetCoinIDParams as PublicTreasuryGetCoinIDParams,
};
export {
Search as Search,
type SearchGetResponse as SearchGetResponse,
type SearchGetParams as SearchGetParams,
};
export { Simple as Simple };
export { TokenLists as TokenLists, type TokenListGetAllJsonResponse as TokenListGetAllJsonResponse };
}