TriportRPC

Errors

Every Triport error — REST or JSON-RPC — uses one predictable JSON envelope with a machine-readable error code, a human message, and a request_id you can quote in support tickets.

Method / Endpointn/a — error reference
NetworkSolana | Ethereum | Polygon
AuthenticationBearer header / x-token header / ?api-key query / path-form
Required scope
Tier / rate limit

Description

Triport returns a consistent error shape across all surfaces. The HTTP status line tells you the broad class of problem, the error field gives you a stable machine-readable code to branch on, and the message is a human-readable explanation safe to log. Some errors carry extra fields (for example, which tier you'd need to upgrade to, or how many seconds to wait before retrying).

Branch your client logic on the error code, not on the message string — messages may be reworded over time, while codes are part of the contract. When you contact support, always include the request_id: it lets us correlate your call with our server-side logs.

JSON-RPC requests (the /sol, /eth, /polygon RPC surfaces) return errors in the standard JSON-RPC error object instead, with a numeric code, a message, and a data payload carrying the same extra fields. The mapping between HTTP error codes and JSON-RPC wire codes is given in JSON-RPC wire codes below.

The error envelope

Every REST error response has this shape:

{
  "error": "tier_insufficient",
  "message": "Method 'getAssetsByOwner' requires basic tier or higher",
  "request_id": "req_9f3c1a8e2b6d4f10"
}

Envelope fields

FieldTypeDescription
errorstringMachine-readable error code. One of the values in the error code table. Branch on this.
messagestringHuman-readable explanation. Safe to log; do not parse.
request_idstringServer-side correlation id. Quote it in support tickets. May be absent on errors generated before a request is fully routed.

Specific error codes add extra top-level fields on top of this base envelope — see the per-code rows below.

Error code table

error codeHTTP statusExtra fieldsWhen it happens
unauthorized401No credentials supplied, or the key/token is invalid or revoked.
trial_expired401expired_at, upgrade_urlThe free 7-day trial period has ended. Upgrade to a paid tier to continue.
subscription_expired401expired_at, renew_urlA previously active paid subscription has lapsed. Renew to restore access.
tier_insufficient403current_tier, required_tier, method, category, upgrade_urlYour tier is below the minimum required for this method. Also sets the X-Required-Tier response header.
method_unknown403chain, methodThe method is not part of the product on this chain.
rate_limited429retry_after_sec, limit_rps, burst_capacity, category, current_tierSustained RPS for your tier and the request's category was exceeded. Sets Retry-After and X-RateLimit-* headers.
invalid_params400The request was malformed: bad/missing parameters or an unparseable body.
internal_error500An unexpected server-side fault. Safe to retry with backoff; quote request_id if it persists.

Note: there is no daily quota and no daily-quota error. Rate limiting is sustained-RPS-per-tier with a burst allowance — see Rate limits.

Extra-field reference

tier_insufficient (403) — adds:

FieldTypeDescription
current_tierstringYour key's tier — one of free, basic, pro, business, enterprise.
required_tierstringMinimum tier needed for this method.
methodstringThe method that was gated.
categorystringThe tier-matrix category bucket that gates this method.
upgrade_urlstring (uri)Link to upgrade your subscription.

Also returned as the X-Required-Tier response header.

rate_limited (429) — adds:

FieldTypeDescription
current_tierstringTier of the API key that triggered the limit.
retry_after_secinteger (≥1)Seconds to wait before retrying. Mirrors the Retry-After header.
limit_rpsinteger (≥1)Sustained RPS allowed for your tier and category.
burst_capacityintegerShort burst ceiling. Equals 2 × limit_rps (global burst multiplier).
categorystringThe tier-matrix category the offending request belongs to.

Also accompanied by the response headers below.

trial_expired (401) — adds expired_at (date-time) and upgrade_url (uri).

subscription_expired (401) — adds expired_at (date-time) and renew_url (uri).

method_unknown (403) — adds:

FieldTypeDescription
chainstringChain the method was attempted on — sol, eth, or polygon.
methodstringThe method name that was not recognised.

Response headers

On 429 rate_limited, Triport sets:

HeaderDescription
Retry-AfterSeconds to wait before retrying.
X-RateLimit-LimitTotal allowed RPS for the matching tier + category.
X-RateLimit-RemainingRemaining capacity in the current 1-second window.
X-RateLimit-ResetUNIX timestamp when the window resets.
X-RateLimit-CategoryTier-matrix category bucket charged for this request.

On 403 tier_insufficient, Triport sets:

HeaderDescription
X-Required-TierMinimum tier required for the method.

Examples

tier_insufficient — 401/403 envelope

{
  "error": "tier_insufficient",
  "message": "Method 'getAssetsByOwner' requires basic tier or higher",
  "request_id": "req_9f3c1a8e2b6d4f10",
  "current_tier": "free",
  "required_tier": "basic",
  "method": "getAssetsByOwner",
  "category": "sol_das",
  "upgrade_url": "https://triport.io/upgrade/basic"
}

rate_limited — 429 envelope

{
  "error": "rate_limited",
  "message": "Rate limit exceeded: 20 RPS sustained on sol_read_rpc (free tier)",
  "request_id": "req_2c7b0e44a1f94d83",
  "current_tier": "free",
  "category": "sol_read_rpc",
  "limit_rps": 20,
  "burst_capacity": 40,
  "retry_after_sec": 1
}

JSON-RPC wire codes

When you call an RPC method, errors arrive inside the standard JSON-RPC error object. The numeric code maps to a Triport error code as follows; the extra fields move into error.data.

JSON-RPC codeTriport codeHTTP status
-32002tier_insufficient403
-32003rate_limited429
-32601method_unknown403

-32601 is the standard JSON-RPC "method not found" code, so generic JSON-RPC clients recognise it without special handling.

tier_insufficient (-32002)

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32002,
    "message": "Method 'getAssetsByOwner' requires basic tier or higher",
    "data": {
      "current_tier": "free",
      "required_tier": "basic",
      "method": "getAssetsByOwner",
      "category": "sol_das",
      "upgrade_url": "https://triport.io/upgrade/basic"
    }
  }
}

rate_limited (-32003)

Returned with HTTP status 429 and a Retry-After: 1 header.

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32003,
    "message": "Rate limit exceeded: 20 RPS sustained on sol_read_rpc (free tier)",
    "data": {
      "current_tier": "free",
      "category": "sol_read_rpc",
      "limit_rps": 20,
      "burst_capacity": 40,
      "retry_after_sec": 1
    }
  }
}

method_unknown (-32601)

{
  "jsonrpc": "2.0",
  "id": 1,
  "error": {
    "code": -32601,
    "message": "Method \"bogus_nonexistent_method_42\" on chain \"polygon\" is not recognised",
    "data": {
      "chain": "polygon",
      "method": "bogus_nonexistent_method_42"
    }
  }
}

Handling errors

JavaScript (fetch)

const res = await fetch("https://api.triport.io/sol", {
  method: "POST",
  headers: {
    "x-token": process.env.TRIPORT_API_KEY,
    "Content-Type": "application/json",
  },
  body: JSON.stringify({
    jsonrpc: "2.0",
    id: 1,
    method: "getBalance",
    params: ["So11111111111111111111111111111111111111112"],
  }),
});


const body = await res.json();


if (body.error) {
  // JSON-RPC error object
  switch (body.error.code) {
    case -32003: {
      const wait = Number(res.headers.get("Retry-After") ?? body.error.data.retry_after_sec);
      await new Promise((r) => setTimeout(r, wait * 1000));
      // ...then retry
      break;
    }
    case -32002:
      console.error("Upgrade required:", body.error.data.upgrade_url);
      break;
    case -32601:
      console.error("Unknown method:", body.error.data.method);
      break;
    default:
      throw new Error(body.error.message);
  }
}

TypeScript SDK (@triport/sdk)

import { Triport, TierInsufficientError, RateLimitedError } from "@triport/sdk";


const client = new Triport({ token: process.env.TRIPORT_API_KEY! });


try {
  await client.sol.getAssetsByOwner("So11111111111111111111111111111111111111112");
} catch (err) {
  if (err instanceof RateLimitedError) {
    await new Promise((r) => setTimeout(r, err.retryAfterSec * 1000));
    // ...retry
  } else if (err instanceof TierInsufficientError) {
    console.error(`Need ${err.requiredTier}; upgrade at ${err.upgradeUrl}`);
  } else {
    throw err;
  }
}

Python (triport-sdk)

import time
from triport import Triport
from triport.errors import RateLimitedError, TierInsufficientError


client = Triport(token=os.environ["TRIPORT_API_KEY"])


try:
    client.sol.get_assets_by_owner("So11111111111111111111111111111111111111112")
except RateLimitedError as e:
    time.sleep(e.retry_after_sec)
    # ...retry
except TierInsufficientError as e:
    print(f"Need {e.required_tier}; upgrade at {e.upgrade_url}")

Notes

  • Branch on codes, not messages. The error code (REST) and JSON-RPC numeric code are stable; message text may change.
  • Respect Retry-After. On 429, wait at least retry_after_sec seconds (or read the Retry-After header) before retrying. burst_capacity is 2 × limit_rps. See Rate limits for the full model.
  • 401 vs 403. A 401 (unauthorized, trial_expired, subscription_expired) is about who you are; a 403 (tier_insufficient, method_unknown) is about what your tier may call. See Authentication for credential setup.
  • internal_error (500) is the only error safe to blindly retry with exponential backoff. If it persists, open a support ticket with the request_id.
  • See also: Getting started · Authentication · Rate limits.