API reference

Wake API

Trace Bitcoin and Ethereum funds from your own code. Send an address, get back a report.

1

Get an API plan

A second plan next to yours. See plans.

2

Create a key

In your dashboard, under API keys. Shown once.

3

Send an address

One POST request. It takes 10 to 60 seconds.

4

Read the report

Risk, flow of funds, named exchanges and mixers.

Base URLhttps://augis.eu/api/v1
FormatHTTPS, JSON in and out.
AuthenticationA secret key: Authorization: Bearer wk_…
PricingAn API plan with a quota of traces. It never uses your credits.

Quickstart

Replace AUGIS_KEY with your key and run this. It traces an Ethereum address and prints the verdict.

curl
curl -X POST https://augis.eu/api/v1/wake/trace \
  -H "Authorization: Bearer $AUGIS_KEY" \
  -H "Content-Type: application/json" \
  --max-time 120 \
  -d '{"address": "0x8589427373D6D84E98730D7795D8f6f8731FDA16", "depth": 3, "direction": "out"}' 

The answer is a JSON report. A trace takes up to about a minute, so give your HTTP client a timeout of at least 120 seconds.

Try it

Run a real trace from your browser. Your key is sent only to Augis and is not stored. This uses one trace of your quota.

Authentication and limits

Your key

  • Send it on every request: Authorization: Bearer wk_…
  • wk_ then 48 hexadecimal characters.
  • 5 active keys at most. Use one per application.
  • We store only a hash. Lost it? Revoke it and make another. Revoking is immediate (401 afterwards).
  • Keep it on your server, never in a web page or an app.

Your quota

  • Monthly = the last 30 days. Daily = the last 24 hours. Rolling, no reset date.
  • Only a finished trace counts. A failure costs nothing.
  • "effort": "thorough" counts 3 traces.
  • API traces are not saved in your history.
  • Check it any time: GET /account.
PlanPrice / 30 daysTraces / 30 daysTraces / dayAt onceRequests / min
Sandbox$152002015
Developer$993,000150120
Production$39915,0007503100
Scale$1,29960,0003,0008300

At once: traces that can run at the same time on your account. Requests / min: per key, on every endpoint.

Trace an address

POST/api/v1/wake/trace

Follows funds from an address, hop by hop, largest flows first, and returns the report. One call is one trace.

Body

FieldTypeDescription
address requiredstringA Bitcoin address (bc1…, 1…, 3…) or an Ethereum address (0x…). The chain is detected for you.
direction"out" | "in""out" (default) follows where funds went. "in" follows where they came from.
depth1 to 6Hops to follow. Default 3.
from, toYYYY-MM-DDOnly transfers between these days (UTC).
minValuenumberIgnore transfers below this value. USD on Ethereum, BTC on Bitcoin.
assets"all" | "native" | "tokens"Ethereum only. ETH only, tokens only, or both.
excludestring[]Up to 25 addresses of the same chain to leave out.
detail"broad" | "standard" | "fine"How small a flow is still followed. "fine" returns the most addresses.
effort"quick" | "standard" | "thorough"How much data to read. "thorough" goes furthest and counts 3 traces.

If result.partial is true, the trace stopped early at the limits of its data source. The report is valid for what it covers: trace again from one of the addresses that were not followed.

Request

curl
curl -X POST https://augis.eu/api/v1/wake/trace \
  -H "Authorization: Bearer $AUGIS_KEY" \
  -H "Content-Type: application/json" \
  --max-time 120 \
  -d '{"address": "0x8589427373D6D84E98730D7795D8f6f8731FDA16", "depth": 3, "direction": "out"}' 

Response, 200

response
{
  "result": {
    "chain": "eth",
    "direction": "out",
    "start": "0x8589427373d6d84e98730d7795d8f6f8731fda16",
    "depth": 3,
    "partial": false,
    "params": { "from": null, "to": null, "minValue": 0, "assets": "all", "exclude": [], "detail": "standard", "effort": "standard", "filtered": "" },
    "generatedAt": "2026-10-15T19:46:51.551Z",
    "tool": { "name": "Wake", "version": "2.0" },
    "risk": "high",
    "summary": { "addresses": 25, "transactions": 40, "apiCalls": 11, "labelled": 7, "expanded": 1, "coverage": 0.669 },
    "verdict": "Of the $629,459 that left this address, 34% went through mixing services, 30% is still held, 33% was not followed further. 3 high-severity alerts.",
    "flags": [{ "severity": "high", "type": "labelled start address", "detail": "The address you entered is itself labelled: tornado.cash (mixing)." }],
    "reached": [{ "address": "0x905b63ff…7601", "hop": 1, "label": "tornado.cash", "categories": ["mixing"], "source": "Augis address intelligence", "received": { "ETH": 123 }, "txCount": null, "reason": "known address" }],
    "held": [{ "address": "0xdcbeffbe…3193", "label": "OFAC-listed address", "balances": { "ETH": 13.5, "USDT": 30108.36, "USDC": 0 }, "freezable": ["USDT"] }],
    "attribution": { "unit": "USD", "total": 629459.13, "buckets": [{ "bucket": "mixing", "label": "Mixing service", "value": 215000.4, "share": 0.34 }], "flows": [] },
    "nodes": [], "edges": [], "timeline": [], "exposure": [], "freeze": [], "caveats": [],
    "sha256": "2fe52c040cfeaa0f15cc2bd99d60f09f3cd996bcd874071ba7b1821607c1e945"
  }
}

Check your usage

GET/api/v1/account

Your API plan and how much of the daily and monthly quota you have used. It does not start a trace.

FieldDescription
planName of your API plan.
expiresAtEnd of the plan, in milliseconds since 1970 (UTC).
dayused in the last 24 hours, and your limit.
monthused in the last 30 days, and your limit.

Request

curl
curl https://augis.eu/api/v1/account \
  -H "Authorization: Bearer $AUGIS_KEY"

Response, 200

response
{
  "plan": "Developer",
  "expiresAt": 1794678374000,
  "day":   { "used": 12, "limit": 150 },
  "month": { "used": 431, "limit": 3000 }
}

The report

Start with these four fields. Everything else is detail you can read when you need it.

risk

One word: high, medium, low or none found.

verdict

The result in a sentence you can show a person.

reached[]

The named places the funds went: exchanges, mixers, sanctioned addresses.

held[]

Where money still sits at the end, and what a stablecoin issuer could review.

Every field

Open an object to see its fields. New fields may appear over time: ignore keys you do not know.

resulttop level
FieldTypeDescription
chain, direction, start, depthWhat you asked for. Ethereum addresses are lower case.
partialbooleantrue when the trace stopped early.
paramsobjectThe filters applied, and filtered, a sentence describing them.
generatedAt, labelsBuilt, tool, sourcesWhen the report was made, when names were last updated, the report version, the kinds of data used.
summaryobjectaddresses, transactions, labelled, expanded, coverage (0 to 1: the share of the value that ends at a known destination).
exposure[]arrayNamed addresses with the path from the start. Risky ones first.
timeline[]arrayday, value, count, top: value moved per day and the largest transaction.
freeze[]arrayStablecoin balances an issuer could review, with a draft request. Outgoing traces only.
caveats[]string[]The limits of the method. Show them with the results.
sha256stringFingerprint of the report. See Verifying a report.
flags[]alerts
FieldTypeDescription
severity"high" | "medium" | "info"How serious.
typestringA short name for the kind of alert.
detailstringWhat was found, in a sentence.
reached[]named places reached
FieldTypeDescription
addressstring
hopintegerDistance from the start address.
labelstring | nullThe name, when known.
categoriesstring[]What kind of address it is.
sourcestring | nullWhere the name comes from.
receivedobjectAmount received per asset, e.g. {"ETH": 123}
txCountinteger | null
reasonstringWhy it is listed.
held[]balances left at the end
FieldTypeDescription
addressstring
labelstring | null
balancesobjectBalance per asset: the native coin and the main stablecoins.
freezablestring[]Stablecoins in the balance that an issuer can restrict, e.g. USDT.
attributionwhere the value went
FieldTypeDescription
unit"USD" | "BTC"USD for Ethereum, BTC for Bitcoin.
totalnumberValue traced, in unit.
buckets[]arraybucket, label, value, share (share is 0 to 1). Buckets: sanctioned, mixing, flagged, exchange, bridge, defi, contract, service, mining, held, beyond.
flows[]arrayfrom, to, value, fromHop, toHop: value moving between two addresses, ready for a flow diagram.
Note
Shares are an estimate: at each address the money is split in proportion to the transfers that left it. They show where value went, not proof that particular coins followed a particular path.
nodes[]up to 500 addresses
FieldTypeDescription
address, hop, label, categoriesAs in reached.
bucketstring | nullThe attribution bucket the address falls in.
terminalstring | nullWhy the trace stopped here: "labelled" (a named service) or "high-volume".
expandedbooleanWhether its transfers were read.
balancesobject | nullPresent where the funds ended.
inflownumberValue attributed to the address, in attribution.unit.
firstSeen, lastSeeninteger | nullUnix seconds.
received, sentobjectTotals per asset within the trace.
edges[]up to 600 transfers
FieldTypeDescription
from, tostringAddresses.
assetstringThe coin or token symbol.
amountnumberIn units of the asset.
usdnumberValue in dollars on the day (Ethereum).
valuenumberThe value used for the shares.
pricedbooleanfalse: the token has no market price, so it is left out of the shares.
txid, time, blockTransaction hash, Unix seconds, block number.
kindstring"transfer", "internal" or "token".
fromLabel, toLabel, fromHop, toHopNames and distances, when known.

Recipes

Copy, set AUGIS_KEY, run. Each one starts from the same small trace() helper.

Is this address risky, and why?

Node.js
const KEY = process.env.AUGIS_KEY;

async function trace(address, extra = {}) {
  const res = await fetch("https://augis.eu/api/v1/wake/trace", {
    method: "POST",
    headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({ address, ...extra }),
    signal: AbortSignal.timeout(120_000),
  });
  const body = await res.json();
  if (!res.ok) throw new Error(`${res.status} ${body.code ?? ""} ${body.error}`);
  return body.result;
}

const report = await trace("0x8589427373D6D84E98730D7795D8f6f8731FDA16");
console.log(report.risk, "-", report.verdict);
for (const f of report.flags.filter((f) => f.severity === "high")) console.log(" !", f.type, "-", f.detail);

Which exchanges did the funds reach?

Node.js
const KEY = process.env.AUGIS_KEY;

async function trace(address, extra = {}) {
  const res = await fetch("https://augis.eu/api/v1/wake/trace", {
    method: "POST",
    headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({ address, ...extra }),
    signal: AbortSignal.timeout(120_000),
  });
  const body = await res.json();
  if (!res.ok) throw new Error(`${res.status} ${body.code ?? ""} ${body.error}`);
  return body.result;
}

const report = await trace("bc1q…your address…", { direction: "out", depth: 4 });
const exchanges = report.reached.filter((r) => r.categories.includes("exchange"));
for (const x of exchanges) console.log(x.label, x.received, `(hop ${x.hop})`);

Is money still sitting somewhere?

Node.js
const KEY = process.env.AUGIS_KEY;

async function trace(address, extra = {}) {
  const res = await fetch("https://augis.eu/api/v1/wake/trace", {
    method: "POST",
    headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({ address, ...extra }),
    signal: AbortSignal.timeout(120_000),
  });
  const body = await res.json();
  if (!res.ok) throw new Error(`${res.status} ${body.code ?? ""} ${body.error}`);
  return body.result;
}

const report = await trace("0x…the address…");
for (const h of report.held) {
  console.log(h.address, h.label ?? "unnamed", h.balances);
  if (h.freezable.length) console.log("  a stablecoin issuer could review:", h.freezable.join(", "));
}

Trace a list without going over your quota

Node.js
const KEY = process.env.AUGIS_KEY;

async function trace(address, extra = {}) {
  const res = await fetch("https://augis.eu/api/v1/wake/trace", {
    method: "POST",
    headers: { Authorization: `Bearer ${KEY}`, "Content-Type": "application/json" },
    body: JSON.stringify({ address, ...extra }),
    signal: AbortSignal.timeout(120_000),
  });
  const body = await res.json();
  if (!res.ok) throw new Error(`${res.status} ${body.code ?? ""} ${body.error}`);
  return body.result;
}

// Stay inside your quota: ask how much is left, then trace that many addresses, one after the other.
const acc = await (await fetch("https://augis.eu/api/v1/account", { headers: { Authorization: `Bearer ${KEY}` } })).json();
const left = Math.min(acc.day.limit - acc.day.used, acc.month.limit - acc.month.used);

const addresses = ["0x…", "0x…", "0x…"].slice(0, left);
for (const a of addresses) {
  const r = await trace(a, { effort: "quick" });
  console.log(a, r.risk);
}

Errors

An error has the HTTP status and a JSON body: {"error": "message", "code": "…"}. code appears only for the cases below.

StatusCodeMeaningWhat to do
400Invalid request: address is missing or is not a Bitcoin or Ethereum address.Fix the request.
401No key, a malformed key, a revoked key or an unknown key.Check the Authorization header.
403NO_API_PLANNo active API plan, or it expired.Buy or renew an API plan.
422The trace could not be completed (a data source did not answer). The message says why. Nothing is counted.Retry later.
429DAILY_LIMITDaily limit reached.Wait, or move to a bigger plan.
429MONTHLY_QUOTA30-day quota reached.Wait, or move to a bigger plan.
429Too many requests per minute on the key, or too many traces at once on the account.Wait a few seconds, retry.
500A problem on our side. Not counted.Retry with a delay.
503Ethereum tracing is unavailable for a moment.Retry with a delay.

Retrying the right errors

Node.js
async function trace(body, tries = 3) {
  for (let attempt = 1; ; attempt++) {
    const res = await fetch("https://augis.eu/api/v1/wake/trace", {
      method: "POST",
      headers: { Authorization: `Bearer ${process.env.AUGIS_KEY}`, "Content-Type": "application/json" },
      body: JSON.stringify(body),
      signal: AbortSignal.timeout(120_000),
    });
    const data = await res.json();
    if (res.ok) return data.result;
    // 429 with no code: too many requests per minute, or a trace already running. Wait and try again.
    // DAILY_LIMIT and MONTHLY_QUOTA will not clear by waiting a few seconds: stop.
    const retryable = res.status === 429 && !data.code || res.status >= 500;
    if (!retryable || attempt === tries) throw new Error(`${res.status} ${data.code ?? ""} ${data.error}`);
    await new Promise((r) => setTimeout(r, 2000 * attempt));
  }
}

Verifying a report

sha256 is the SHA-256 of the report without that field, written as JSON in the order the fields arrive. Recompute it to check that a saved report was not edited.

Node.js
import { createHash } from "node:crypto";

const { sha256, ...rest } = result;
const ok = createHash("sha256").update(JSON.stringify(rest)).digest("hex") === sha256;

This proves the file is unchanged since Wake produced it, not that its content is correct. Another language gets the same hash only if it writes numbers exactly as JavaScript does: verify with Node.js or in Wake.

Good practice

Be robust

  • Timeout of 120 seconds. Never retry a request that is still running: it will finish and count.
  • Retry 429 without a code and 5xx. Do not retry DAILY_LIMIT or MONTHLY_QUOTA.
  • Keep keys server-side. Rotate one if a laptop or a repository is exposed.

Be economical

  • A report does not change once made: store it.
  • Narrow with from, to, minValue and exclude before reaching for "fine" or "thorough".
  • Pass caveats along when you show results to someone else.

Changelog

October 2026 · v1

First release: POST /api/v1/wake/trace and GET /api/v1/account, API plans with daily and monthly quotas, API keys in the dashboard.

Support

A question, a limit that gets in your way, a plan that does not fit: contact@augis.eu. The Parallax API is in early access: write to the same address to join the waitlist.