Docs menu
- Docs
- Guides
- Your account
Your account
Everything about your account lives under /v1/account on https://api.megalania.io: balance, keys, people, usage, request records, purchases, top-ups and the referral program. Sign in with Google or GitHub, make an account key (meg_ak_…) on the Keys page, and send it in the Authorization header. Only GET /v1/account also takes your scrape key.
Every example reads MEG_ACCOUNT_KEY from the environment. Get a key shows
where the key comes from. The account reference lists
every route, generated from the API contract.
Keys and scopes
A key has one scope, chosen when it is made. The prefix shows it: meg_sk_ is a scrape key, meg_ak_ is an account key. The rest is 43 random characters. We store only a hash and the last 4 characters, so we can never show a key again.
| scope | works on | does not work on |
|---|---|---|
scrape |
/v1 (scraping), batches, GET /v1/account |
the rest of /v1/account: 403 wrong_scope |
account |
everything under /v1/account |
scraping, batches, proxy: 403 wrong_scope |
- Header only. Send
Authorization: Bearer $MEG_ACCOUNT_KEY. Theapi_keyquery parameter is refused here with 401bad_token, because a key in a URL ends up in logs and browser history. - A revoked key stops working in about 1 second. A new key works within about a second.
- Every answer carries
Cache-Control: no-storeandX-Request-Id. There are no CORS headers: call this API from your server, never from a browser page. - Every write takes JSON. Send
Content-Type: application/json, even with an empty{}body. Bodies are at most 4 KiB. A key the request does not take is a 400bad_parameternaming it. - 10 requests a second per key, bursts of 20. Above that: 429
rate_limitedwithRetry-After.
Roles
A person in the account is an owner or a member.
| what | owner | member |
|---|---|---|
| read the account, usage, the member list | yes | yes (no emails) |
| make a key for themselves | yes | yes |
| list and revoke keys | every key | their own keys |
| read request records | every request | requests made with their own keys |
| add people, change roles | yes | no: 403 owner_only |
| remove a person | anyone | only themselves (leave) |
| read purchases, top up | yes | no: 403 owner_only |
| the referral program | yes | no: 403 owner_only |
The account always keeps at least one owner: 409 last_owner otherwise.
Balance and limits
curl -s -H "Authorization: Bearer $MEG_ACCOUNT_KEY" "https://api.megalania.io/v1/account"
{
"id": "0199b0a1-0000-7000-8000-00000000a001",
"created_at": "2026-09-23T10:00:00Z",
"balance": 98750,
"held": 150,
"grant": null,
"plan": { "name": "prepaid", "concurrency": 5, "max_members": 10, "max_keys": 20 },
"caps": {
"daily_cost_cap": { "reached": false, "resets_at": "2026-09-24T00:00:00Z" },
"period_cost_cap": { "reached": false, "resets_at": null },
"free_failures": { "used_percent": 12, "reached": false, "resets_at": "2026-09-24T00:00:00Z" },
"spend_cap": { "cap": 50000, "used": 12500, "used_percent": 25, "reached": false, "resets_at": "2026-10-01T00:00:00Z" }
},
"default_country": "DE",
"found_us": "friend",
"country_in_use": "DE",
"premium_credits": 3,
"alerts": { "webhook_url": "https://hooks.example.com/megalania", "email": false, "email_available": false },
"webhook_secret": { "set": true, "rotated_at": "2026-09-20T08:00:00Z", "previous_valid_until": null },
"subscription": null,
"caller": { "person_id": "0199b0a1-...", "role": "owner", "key_id": "0199b0a1-...", "scope": "account" }
}balance: credits you can spend now.held: credits reserved by requests still running. They go back tobalance, or are charged, when those finish.grant: the part ofbalancefrom a free grant, with its end date.nullwhen there is none.plan: your limits.caps: whether a usage limit is reached, named like the 429 you would get, and when it resets.free_failures: how much of today’s free browser and residential retries you used, in percent; atreachedsuch requests answer 429free_failures_spentuntil 00:00 UTC.default_country: the country requests withoutcountry_codeexit from, set by an owner.nullmeans the platform default.country_in_useis the country used now; it differs when we no longer serve your choice. See Countries.premium_credits: the extra credits per request on a premium country.found_us: how an owner said the account found us:search,ai_assistant,friend,social,community,directory,blogorother.nulluntil they answer. Set it once withPATCH /v1/account {"found_us": "friend"}; the first answer is kept and a later one is ignored. It is optional, and only an owner sets it.caps.spend_cap: your monthly spend cap and this UTC month’s use against it, named like the 402 you would get. See Spending cap.alerts: where usage alerts go, and whether we can send email yet. See Usage alerts.webhook_secret: whether a secret exists and when it was rotated. Never the secret itself. See Webhook secret.subscription: your latest subscription (plan,status,period_end,cancel_at_period_end), ornullwhen you never subscribed.caller: who you are: yourperson_id, your role, the key you sent.
It is free. $MEG_API_KEY works here too.
Keys
List them. An owner sees every key, a member sees their own:
curl -s -H "Authorization: Bearer $MEG_ACCOUNT_KEY" "https://api.megalania.io/v1/account/keys"
The list gives each key’s kind (api, mcp or dashboard) and hint (its last 4 characters). mcp marks a key made by an MCP connection (Claude, ChatGPT). The list never holds the key itself.
Make one. expires_at is required, in the future and at most 400 days
ahead. name is 1 to 64 characters, default the scope word. scope is
scrape (the default) or account:
curl -s -X POST "https://api.megalania.io/v1/account/keys" \
-H "Authorization: Bearer $MEG_ACCOUNT_KEY" \
-H "Content-Type: application/json" \
-d '{"name": "laptop", "scope": "scrape", "expires_at": "2027-01-01T00:00:00Z"}'The answer is 201 with api_key (a meg_sk_... or meg_ak_... string), shown once, and key, its record. A key
always belongs to the person who made it. When that person leaves the
account, their keys are revoked. Too many keys is 402 over_plan.
A new key works within about 1 second. A script that uses it right away
waits 1 second first, or retries a 401 once after 1 second.
Revoke one. It stops working in about 1 second:
export KEY_ID='<id from the list>' curl -s -X DELETE -H "Authorization: Bearer $MEG_ACCOUNT_KEY" \ "https://api.megalania.io/v1/account/keys/$KEY_ID"
The answer is the key as it was. A key you cannot see is 404 not_found.
People
Members are API only in the beta: the dashboard has no Members page yet.
List them. Owners see emails; for members email is null:
curl -s -H "Authorization: Bearer $MEG_ACCOUNT_KEY" "https://api.megalania.io/v1/account/members"
Add one (owner). role is member (the default) or owner. We send no
email:
curl -s -X POST "https://api.megalania.io/v1/account/members" \
-H "Authorization: Bearer $MEG_ACCOUNT_KEY" \
-H "Content-Type: application/json" \
-d '{"email": "[email protected]"}'The answer is 201 with the new person_id. An email that is already in this
account is 409 already_member. Too many people is 402 over_plan.
The new member signs in with Google or GitHub with that email and lands in this account. They make their own keys on the Keys page. Never send a key by chat or email.
Change a role (owner):
export PERSON_ID='<person_id from the list>'
curl -s -X PATCH "https://api.megalania.io/v1/account/members/$PERSON_ID" \
-H "Authorization: Bearer $MEG_ACCOUNT_KEY" \
-H "Content-Type: application/json" \
-d '{"role": "owner"}'Remove someone (owner), or leave (your own person_id):
curl -s -X DELETE -H "Authorization: Bearer $MEG_ACCOUNT_KEY" \ "https://api.megalania.io/v1/account/members/$PERSON_ID"
{ "person_id": "0199b0a1-...", "keys_revoked": 2, "items_canceled": 40 }Removing a person revokes every key they made and cancels their batch items that have not started, in one step.
Usage
Requests and credits per UTC day, newest first. days is 1 to 90, default
30. Today is included and still growing:
curl -s -H "Authorization: Bearer $MEG_ACCOUNT_KEY" "https://api.megalania.io/v1/account/usage?days=7"
{ "days": 7, "usage": [
{ "date": "2026-09-23", "requests": 1200, "delivered": 1100, "blocked": 60, "credits_charged": 5400, "credits_refunded": 40,
"by_source": { "api": { "requests": 1000, "credits": 4600 }, "mcp": { "requests": 150, "credits": 600 }, "job": { "requests": 50, "credits": 200 } } }
] }credits_charged is what you paid that day, already net: a refunded request
adds 0 to it. credits_refunded is for information; do not subtract it again.
by_source splits the day: job is a batch item, mcp a request made with a
key from an MCP sign-in (Claude, ChatGPT), api everything else.
Request records
One record per request, newest first, the last 90 days. limit is 1 to 100,
default 50. key_id keeps one key’s requests; source (api, mcp, job
or monitor) keeps one source’s, and api also matches proxy; host (like
example.com, any case) keeps one site’s. With any filter the list covers the
last 7 days, not 90:
curl -s -H "Authorization: Bearer $MEG_ACCOUNT_KEY" "https://api.megalania.io/v1/account/requests?limit=50"
Each record has id (the X-Request-Id you got), created_at, key_id,
key_name (null once the key is deleted), source (api, proxy, job,
monitor or mcp), client (claude or chatgpt when source is mcp), job_id,
monitor_id (the monitor when source is monitor),
engine, target_status,
error_code (a code from errors.md, null when delivered),
error_message (the one-line message for that code, the same text the API
answered with; absent when delivered),
blocked, challenge, credits_charged, duration_ms, bytes and
target_host: the site’s host name only, like example.com. We never store
the path, the query or the full URL. It is null when your account keeps no
hosts (audit level minimal).
A record is mcp while its key exists. Once an MCP key is deleted (unused for
30 days, expired, or replayed), its records show as api here. The usage
totals keep them as mcp once their hour is rolled up, about an hour after the
hour ends, because the rollup fixes the source.
One request
GET /v1/account/requests/{id} gives one record, by the X-Request-Id you
got, with more detail: the climb and where the credits came from.
curl -s -H "Authorization: Bearer $MEG_ACCOUNT_KEY" "https://api.megalania.io/v1/account/requests/<request id>"
{"id": "...", "credits_charged": 12, "route": "h:dc>c:dc",
"hops": [{"rung": "h:dc", "identity": "firefox", "ms": 310, "outcome": "blocked"},
{"rung": "c:dc", "identity": "chrome", "ms": 1200, "outcome": "ok"}],
"credit_parts": {"route": 9, "output": 2, "other": 1},
"llm_tokens": 0, "response_types": ["markdown", "json"], "...": "the list fields"}hopsis each route we tried, in order, the same as theX-Hopsheader. Every try before the last was not delivered and cost nothing. It isnullwhen no route was tried.credit_partsis the same split as theX-Credits-Route,X-Credits-OutputandX-Credits-Otherheaders (Credits).route + output + otheriscredits_charged. It isnullfor requests from before we kept the parts.llm_tokensis the language-model tokens the request used, andresponse_typesthe outputs you asked for.- A member sees only the requests of their own keys. Any other id is a 404,
never a 403. The dashboard reads the same record at
GET /api/account/requests/{id}.
Every list here pages the same way: send next_cursor back as cursor until
it is null.
curl -s -H "Authorization: Bearer $MEG_ACCOUNT_KEY" \ "https://api.megalania.io/v1/account/requests?limit=50&cursor=<next_cursor>"
What we keep about your requests. For each request we keep a record: its id, time, key, engine, result, credits and the target’s host name (like shop.example.com). We never write the full target URL, its path or query, your request headers, cookies, or your IP address into a log or a table. A batch is the one exception you choose: it needs its URLs to run them, so it keeps them while it runs and deletes them 7 days after it ends (see Batches). GET /v1/account/requests shows you your records.
Purchases
Owner only. Paid purchases, newest first:
curl -s -H "Authorization: Bearer $MEG_ACCOUNT_KEY" "https://api.megalania.io/v1/account/purchases"
{ "purchases": [
{ "id": "0199b0a1-...", "created_at": "2026-09-23T10:00:00Z", "credits": 100000,
"amount": "25.00", "currency": "USD", "status": "paid", "paid_by": null,
"campaign": "reddit-launch", "receipt_url": null }
], "next_cursor": null }amount is a decimal string. paid_by is the person who bought, null
once that person has left the account. receipt_url is always
null today.
Spending cap
Owner only. A cap is the most credits this account may spend in one UTC month. Requests, batch items, monitor checks and LLM steps all count, as charged, plus what requests still running have reserved.
curl -s -X PATCH "https://api.megalania.io/v1/account" \
-H "Authorization: Bearer $MEG_ACCOUNT_KEY" \
-H "Content-Type: application/json" \
-d '{"spend_cap": 50000}' | jq .caps.spend_cap{ "cap": 50000, "used": 12500, "used_percent": 25, "reached": false, "resets_at": "2026-11-01T00:00:00Z" }- A request that would take this month past the cap answers
402 spend_cap. It costs nothing, and the message says when it resets. A batch item endsfailedwith the same error; a monitor check is skipped for free. - The cap resets at 00:00 UTC on the 1st. Send
"spend_cap": nullto remove it. A member can read the cap but not change it. - The cap counts credits, not money: it is the same number you see in
X-Credits-Charged. - The dashboard sets it on Plan and billing, and shows a banner at 80% and 100%.
Usage alerts
We tell you at 50, 80 and 100% of your spend cap. With no cap set, we count against your last grant or top-up instead. Each step is sent once. We also tell you once when a request is stopped: by the cap, or, with no cap, because the balance could not cover it.
curl -s -X PATCH "https://api.megalania.io/v1/account" \
-H "Authorization: Bearer $MEG_ACCOUNT_KEY" \
-H "Content-Type: application/json" \
-d '{"alerts": {"webhook_url": "https://hooks.example.com/megalania"}}'webhook_urlgets a signedPOST, like the batch webhook (same secret, same headers,X-Megalania-Event: account.usage). It must behttpson a public host.nullremoves it. A failed delivery is tried again after 1 s, 5 s, 25 s and 125 s.email: truemails every owner whose email is verified. Email is not available yet:alerts.email_availableisfalse, andemail: trueis refused with400 bad_parameteruntil it is.
{
"event": "account.usage",
"kind": "used",
"used_percent": 80,
"base": "spend_cap",
"spend_cap": 50000,
"resets_at": "2026-11-01T00:00:00Z",
"created_at": "2026-10-22T09:14:03Z"
}kindisused(a step was reached) orstopped(a request was refused;used_percentis the real use before it).baseisspend_caporgrant. The body then carriesspend_cap(the cap) orgrant(the credits of the last grant).resets_atisnullfor a grant.- The body names no host and no URL.
Webhook secret
Every webhook we send you (batches, monitors, usage alerts) is signed with one secret per account. We show it once, when it is made, and never again:
curl -s -X POST "https://api.megalania.io/v1/account/webhook-secret/rotate" \ -H "Authorization: Bearer $MEG_ACCOUNT_KEY"
{ "secret": "bmV3LXNlY3JldC1ieXRlcy0wMTIzNDU2Nzg5YWJjZGVmZ2g=", "previous_valid_until": "2026-10-23T09:14:03Z" }- Owner only, with an account key, or on the dashboard: Connect, Webhooks, Rotate and show.
- Store it in your secret manager at once. Lost it? Rotate again.
- For 24 hours the old secret also signs: the signature header carries both values, newest first. Update your receivers in that window. A second rotation inside it drops the oldest secret.
- Leaked? Rotate twice. The second rotation ends the leaked secret at once, instead of after 24 hours.
- At most 5 rotations an hour.
- How to check a signature: Batches, check the signature.
Webhooks
Send a signed test event to check your receiver:
curl -s -X POST "https://api.megalania.io/v1/account/webhooks/test" \
-H "Authorization: Bearer $MEG_ACCOUNT_KEY" -H "Content-Type: application/json" \
-d '{}'{ "status": 200, "error": null, "duration_ms": 87 }{}sends it to youralerts.webhook_url. To try another receiver, send{"url": "<https URL>"}. It must be one https URL on a public host.- The body is
{"id", "event": "test", "created_at"}, signed like every webhook, withX-Megalania-Event: test. We send it once, with no retry. - The answer is what your receiver said:
statusis its HTTP status,nullwhen it did not answer. Thenerrorsays why:timeout,connect,private(the host is not on the public internet) orrequest. - It is free. At most 5 a minute per account.
- Every member can send a test; only an owner rotates.
Subscribe
Owner only. GET /v1/plans lists the plans (no key needed). The first one,
free, is the free tier: its price and currency are null, and it
needs no subscription. Subscribe to one of the others:
curl -s -X POST "https://api.megalania.io/v1/account/subscription" \
-H "Authorization: Bearer $MEG_ACCOUNT_KEY" \
-H "Content-Type: application/json" \
-d '{"plan": "starter"}' | jq -r .checkout_urlOpen the printed link and pay. Each paid month adds the plan’s monthly
credits; they last 61 days. With a subscription, the same call changes the
plan: a higher plan is charged in full now and starts a new month with its
full credits ({"change": "upgrade"}); a lower plan starts at your next
renewal ({"change": "downgrade"}). Your card, your invoices and canceling
are on the billing portal: POST /v1/account/billing-portal returns
portal_url. A cancel takes effect at the end of the paid month, and your
credits stay until they expire.
Top up
Owner only. A pack tops up an active subscription and is sold only for your
plan; without a subscription the answer is 409 no_subscription. It opens a
Stripe payment page whose credits land on this account. The dashboard’s
Plan and billing page does the same, under Top up. It makes no keys:
curl -s -X POST "https://api.megalania.io/v1/account/checkout" \
-H "Authorization: Bearer $MEG_ACCOUNT_KEY" \
-H "Content-Type: application/json" \
-d '{"pack": "starter"}' | jq -r .checkout_urlOpen the printed link in a browser and pay. The credits show in balance
once Stripe confirms the payment.
A body may carry ref_token (from a referral link) or ref_code (a code
someone gave you). Only the account’s first paid purchase binds a
referral; a ref on a later purchase is dropped. A bad ref is never an
error.
Referral program
Owner only. Share your link; when an account you referred pays real money, you earn 12% of what it paid, as credits, never cash.
- Your account can join after one paid purchase that was not refunded or
disputed. Otherwise: 403
not_eligible. - Earnings wait 60 days after each payment, then are paid as credits at the list price once a month.
- It counts the referred account’s purchases in its first 12 months, up to $250 per referred account.
- Nothing is earned on your own account, an account that shares a person with yours, an account whose people already belong to another account, a free account, or a click from an ad. We match people by their sign-in email, never by the email typed into Stripe.
Join. It makes your first code. Sending it again changes nothing:
curl -s -X POST "https://api.megalania.io/v1/account/affiliate" \
-H "Authorization: Bearer $MEG_ACCOUNT_KEY" \
-H "Content-Type: application/json" -d '{}'Your link is link in the answer: https://api.megalania.io/r/<code>. Add
?campaign=<name> to see which post it came from, and to=/buy to send
people straight to the Buy credits page. The referral binds when the
referred account makes its first paid purchase, after it signs in.
Read your status, terms, codes and earnings:
curl -s -H "Authorization: Bearer $MEG_ACCOUNT_KEY" "https://api.megalania.io/v1/account/affiliate"
Make another code (at most 5):
curl -s -X POST "https://api.megalania.io/v1/account/affiliate/codes" \
-H "Authorization: Bearer $MEG_ACCOUNT_KEY" \
-H "Content-Type: application/json" -d '{}'Clicks, referrals and earnings per month, months 1 to 12, default 6:
curl -s -H "Authorization: Bearer $MEG_ACCOUNT_KEY" "https://api.megalania.io/v1/account/affiliate/stats?months=3"
Before you join, these three answer 404 not_enrolled.
Errors
| HTTP | error.code |
what to do |
|---|---|---|
| 400 | bad_parameter |
Read error.field and the message. |
| 400 | bad_request |
Send one JSON object. |
| 401 | bad_token |
Send your key as Authorization: Bearer <key>. Not in the query. |
| 402 | over_plan |
Revoke a key or remove a person, then try again. |
| 402 | spend_cap |
Your monthly spend cap is reached. Raise it with PATCH /v1/account, or wait for the 1st (UTC). Nothing was charged. |
| 403 | wrong_scope |
Use your account key, not your scrape key. |
| 403 | owner_only |
Ask an owner. |
| 404 | not_found |
No key, person or pack with that id or name in this account. |
| 409 | already_member |
The email is already here. Find its person_id in the list. |
| 409 | last_owner |
Make someone else an owner first. |
| 429 | rate_limited |
Wait the seconds in Retry-After. |
| 503 | busy |
Send the same request again in a few seconds. |
The referral codes not_eligible and not_enrolled, and every other code
with its cost, are in errors.md.