List invoices
GET/v1/invoices
Contract v1 / 1.0.0 62 / 2026-10-10
On this page
Invoices only, not donations. Newest ID first. The response contains data without next_before; pass the last item ID as before to continue. Stop at an empty page.
Key permissions: invoices / read / payments.
Parameters
| Parameter | In | Required | Description |
|---|---|---|---|
project | query | No | Project ID. Required for listing or creating watches unless the key is project-scoped. For other operations omission uses the operation's account/project context; set it explicitly.string |
chain | query | No | Network code filter.string |
status | query | No | Invoice status.string pending, seen, paid, underpaid, partial, overpaid, expired, late, canceled |
limit | query | No | Page size: at most 200. Invalid values use the default; values above 200 are capped.integer Default: 50; minimum: 1; maximum: 200 |
before | query | No | Cursor from next_before or the last item's ID on the previous page; newest first.string minLength: 1 |
Request examples
Examples perform a real operation in your account. Set the environment variables named in the code. Keep your key private; write requests are not retried automatically.
#!/usr/bin/env bash
set -euo pipefail
: "${CRYPTANIO_PROJECT_ID:?Set CRYPTANIO_PROJECT_ID}"
: "${CRYPTANIO_API_KEY:?Set CRYPTANIO_API_KEY}"
response_file="$(mktemp)"
trap 'rm -f "$response_file"' EXIT
transfer_status=0
http_status="$(curl \
--silent --show-error --max-time 30 --request GET \
--header "Authorization: Bearer $CRYPTANIO_API_KEY" \
--header 'Accept: application/json' \
--get \
--data-urlencode "project=$CRYPTANIO_PROJECT_ID" \
--data-urlencode 'limit=50' \
"https://api.cryptanio.com/v1/invoices" \
--output "$response_file" --write-out '%{http_code}')" || transfer_status=$?
if [[ "$http_status" == 2?? ]]; then
cat "$response_file"
exit "$transfer_status"
fi
printf 'HTTP %s\n' "$http_status" >&2
cat "$response_file" >&2
exit 1
// Go; standard library only. Run: go run request.go
package main
import (
"fmt"
"io"
"net/http"
"net/url"
"os"
"time"
)
func env(name string) string {
value := os.Getenv(name)
if value == "" { fmt.Fprintln(os.Stderr, "Set", name); os.Exit(1) }
return value
}
func run() error {
path := "/v1/invoices"
endpoint, err := url.Parse("https://api.cryptanio.com" + path)
if err != nil { return err }
query := endpoint.Query()
query.Set("project", env("CRYPTANIO_PROJECT_ID"))
query.Set("limit", "50")
endpoint.RawQuery = query.Encode()
request, err := http.NewRequest("GET", endpoint.String(), nil)
if err != nil { return err }
request.Header.Set("Authorization", "Bearer " + env("CRYPTANIO_API_KEY"))
request.Header.Set("Accept", "application/json")
client := &http.Client{Timeout: 30 * time.Second, CheckRedirect: func(*http.Request, []*http.Request) error { return http.ErrUseLastResponse }}
response, err := client.Do(request)
if err != nil { return err }
defer response.Body.Close()
if response.StatusCode < 200 || response.StatusCode >= 300 {
body, err := io.ReadAll(response.Body)
if err != nil { return err }
return fmt.Errorf("HTTP %d: %s", response.StatusCode, body)
}
_, err = io.Copy(os.Stdout, response.Body)
return err
}
func main() {
if err := run(); err != nil { fmt.Fprintln(os.Stderr, err); os.Exit(1) }
}
// Cargo.toml dependencies:
// reqwest = { version = "=0.12.24", features = ["blocking", "json"] }
// serde_json = "=1.0.145"
use std::{error::Error, io, time::Duration};
use reqwest::{blocking::Client, redirect::Policy, Method, Url};
fn env(name: &str) -> Result<String, Box<dyn Error>> {
let value = std::env::var(name)?;
if value.is_empty() { return Err(format!("Set {name}").into()); }
Ok(value)
}
fn run() -> Result<(), Box<dyn Error>> {
let mut url = Url::parse("https://api.cryptanio.com")?;
{
let mut segments = url.path_segments_mut().map_err(|_| "Invalid base URL")?;
segments.clear();
segments.push("v1");
segments.push("invoices");
}
url.query_pairs_mut()
.append_pair("project", &env("CRYPTANIO_PROJECT_ID")?)
.append_pair("limit", &"50".to_owned());
let client = Client::builder().timeout(Duration::from_secs(30)).redirect(Policy::none()).build()?;
let request = client.request(Method::GET, url)
.header("Authorization", format!("Bearer {}", env("CRYPTANIO_API_KEY")?))
.header("Accept", "application/json".to_owned());
let mut response = request.send()?;
let status = response.status();
if !status.is_success() { return Err(format!("HTTP {}: {}", status.as_u16(), response.text()?).into()); }
io::copy(&mut response, &mut io::stdout())?;
Ok(())
}
fn main() {
if let Err(error) = run() { eprintln!("{error}"); std::process::exit(1); }
}
<?php
// PHP 8 with the cURL extension. Run this on your server.
function env(string $name): string {
$value = getenv($name);
if ($value === false || $value === '') throw new RuntimeException('Set ' . $name);
return $value;
}
try {
$path = '/v1/invoices';
$query = [
'project' => env('CRYPTANIO_PROJECT_ID'),
'limit' => '50',
];
$url = 'https://api.cryptanio.com' . $path . ($query ? '?' . http_build_query($query, '', '&', PHP_QUERY_RFC3986) : '');
$headers = [
'Authorization: ' . 'Bearer ' . env('CRYPTANIO_API_KEY'),
'Accept: ' . 'application/json',
];
$curl = curl_init($url);
curl_setopt_array($curl, [
CURLOPT_CUSTOMREQUEST => 'GET',
CURLOPT_HTTPHEADER => $headers,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_FOLLOWLOCATION => false,
CURLOPT_CONNECTTIMEOUT => 10,
CURLOPT_TIMEOUT => 30,
]);
$response = curl_exec($curl);
if ($response === false) throw new RuntimeException(curl_error($curl));
$status = curl_getinfo($curl, CURLINFO_HTTP_CODE);
if ($status < 200 || $status >= 300) throw new RuntimeException('HTTP ' . $status . ': ' . $response);
echo $response;
} catch (Throwable $error) {
fwrite(STDERR, $error->getMessage() . PHP_EOL);
exit(1);
}
# Python 3; standard library only.
import json
import os
import sys
from urllib.error import HTTPError, URLError
from urllib.parse import quote, urlencode
from urllib.request import HTTPRedirectHandler, Request, build_opener
def env(name):
result = os.environ.get(name)
if not result:
raise ValueError("Set " + name)
return result
class NoRedirect(HTTPRedirectHandler):
def redirect_request(self, req, fp, code, msg, headers, newurl):
return None
try:
path = "/v1/invoices"
query = {
"project": env("CRYPTANIO_PROJECT_ID"),
"limit": "50",
}
url = "https://api.cryptanio.com" + path + ("?" + urlencode(query) if query else "")
headers = {
"Authorization": "Bearer " + env("CRYPTANIO_API_KEY"),
"Accept": "application/json",
}
data = None
request = Request(url, data=data, headers=headers, method="GET")
with build_opener(NoRedirect()).open(request, timeout=30) as response:
if not 200 <= response.status < 300:
raise ValueError("HTTP " + str(response.status))
sys.stdout.buffer.write(response.read())
except HTTPError as error:
print("HTTP " + str(error.code) + ": " + error.read().decode("utf-8", errors="replace"), file=sys.stderr)
sys.exit(1)
except (ValueError, URLError, OSError) as error:
print(str(error), file=sys.stderr)
sys.exit(1)
// Node.js 18+. Run on your server, never in a browser.
const env = name => {
const result = process.env[name];
if (!result) throw new Error(`Set ${name}`);
return result;
};
try {
let path = "/v1/invoices";
const url = new URL(path, "https://api.cryptanio.com");
url.searchParams.set("project", env("CRYPTANIO_PROJECT_ID"));
url.searchParams.set("limit", "50");
const headers = {
"Authorization": "Bearer " + env("CRYPTANIO_API_KEY"),
"Accept": "application/json",
};
const response = await fetch(url, {
method: "GET", headers, redirect: "manual",
signal: AbortSignal.timeout(30_000),
});
if (!response.ok) throw new Error(`HTTP ${response.status}: ${await response.text()}`);
if (response.body) {
for await (const chunk of response.body) process.stdout.write(chunk);
}
} catch (error) {
console.error(error.message);
process.exitCode = 1;
}
Responses and errors
200 Successful response.
| Field | Type | Required | Description |
|---|---|---|---|
data | object[] | Yes |
401 No valid active API key (unauthorized).
| Field | Type | Required | Description |
|---|---|---|---|
error | object | Yes |
{
"error": {
"code": "forbidden",
"message": "this API key is read-only"
}
}403 Key disallows the operation, project, service or IP; email unconfirmed.
| Field | Type | Required | Description |
|---|---|---|---|
error | object | Yes |
{
"error": {
"code": "forbidden",
"message": "this API key is read-only"
}
}429 Too many requests (rate_limited); retry after Retry-After.
| Field | Type | Required | Description |
|---|---|---|---|
error | object | Yes |
{
"error": {
"code": "forbidden",
"message": "this API key is read-only"
}
}500 Internal error (internal).
| Field | Type | Required | Description |
|---|---|---|---|
error | object | Yes |
{
"error": {
"code": "forbidden",
"message": "this API key is read-only"
}
}503 Temporary unavailability (unavailable); retry with backoff.
| Field | Type | Required | Description |
|---|---|---|---|
error | object | Yes |
{
"error": {
"code": "forbidden",
"message": "this API key is read-only"
}
}Nested objects
InvoiceList
| Field | Type | Required | Description |
|---|---|---|---|
data | object[] | Yes |
Invoice
| Field | Type | Required | Description |
|---|---|---|---|
checkout_url | string | No | Hosted Cryptanio payment page. Creation/retry of a payer-selected invoice returns an interactive capability that permits method selection until payment is detected. Fixed-method creation and every GET/list return a separate read-only link. Share either link only with the intended payer; neither grants account or API-key access. Confirm payment using the invoice API or a verified webhook, not a browser redirect.uri |
id | string | Yes | Opaque identifier; keep it as a string. minLength: 1 |
status | string | Yes | pending awaits payment; seen detected; partial partially paid; paid settled; underpaid/overpaid/late need a decision; expired deadline passed; canceled voided.pending, seen, paid, underpaid, partial, overpaid, expired, late, canceled |
project_id | string | Yes | Opaque identifier; keep it as a string. minLength: 1 |
reference | string | No | Order reference. |
customer | object | No | |
product | object | No | |
payer | object | No | |
metadata | object | No | Up to 20 pairs, at most 8192 JSON bytes. Do not include secrets. |
invoice_currency | string | No | Order quote currency. |
invoice_amount | string | No | Exact decimal amount as a string; do not use floating point. pattern: ^[0-9]+(?:\.[0-9]+)?$ |
chain | string | Yes | Network code. |
asset | string | Yes | Payment asset. |
address | string | Yes | Payment address. |
payment_route | object | No | Required on-chain discriminator when an automatic source issues a tag, memo, TON comment or Solana Pay reference. Keep it with the receiving address; the merchant order reference is a separate field. |
payment_uri | string | No | Solana Pay transfer request with the recipient, exact amount, supported token mint and mandatory reference. Encode this URI in the payment QR instead of the plain receiving address.uri |
amount | string | string | Yes | Actual amount the buyer must send; may differ from asked to distinguish invoices. Empty until a method is selected. |
amount_raw | string | string | Yes | Integer smallest-unit amount as a string. Empty until a method is selected. |
asked | string | string | Yes | Originally requested crypto amount. Empty until a method is selected. |
decimals | integer | Yes | Asset decimal places. minimum: 0 |
tolerance | string | No | Tolerance in smallest units; unlike request.tolerance. pattern: ^[0-9]+$ |
sandbox | boolean | Yes | A test invoice does not prove a real blockchain payment. |
needs_decision | boolean | No | A decision is required for underpaid, overpaid, or late. |
paid | string | No | Received amount in smallest units. pattern: ^[0-9]+$ |
tx_id | string | No | Payment transaction identifier. |
paid_at | string | No | UTC date and time in RFC 3339.date-time |
expires_at | string | Yes | UTC date and time in RFC 3339.date-time |
created_at | string | Yes | UTC date and time in RFC 3339.date-time |
ID
Opaque identifier. Its format is not guaranteed; do not convert to a number.
string Opaque identifier. Its format is not guaranteed; do not convert to a number.
InvoiceStatus
pending awaits payment; seen detected; partial partially paid; paid settled; underpaid/overpaid/late need a decision; expired deadline passed; canceled voided.
string pending awaits payment; seen detected; partial partially paid; paid settled; underpaid/overpaid/late need a decision; expired deadline passed; canceled voided.
Customer
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Customer identifier in your system. maxLength: 120 |
Product
| Field | Type | Required | Description |
|---|---|---|---|
id | string | No | Product identifier in your system. maxLength: 120 |
name | string | No | Product name. maxLength: 200 |
quantity | integer | No | Quantity. minimum: 1; maximum: 1000000 |
Payer
| Field | Type | Required | Description |
|---|---|---|---|
comment | string | Yes | Payer comment. maxLength: 500 |
InvoiceMetadata
Up to 20 pairs, at most 8192 JSON bytes. Do not include secrets.
object Up to 20 pairs, at most 8192 JSON bytes. Do not include secrets.
Decimal
string
RawAmount
string
Error
| Field | Type | Required | Description |
|---|---|---|---|
error | object | Yes |