Everything you do in the Amóni portal you can also do over HTTP. The same servers, cloud instances, IP and reverse DNS, monitors and projects sit behind one REST API at https://api.amoni.app/v1, so a deploy pipeline, a Terraform run or a one-line curl can drive your account without a browser. This page gets you from nothing to a working, authenticated call.
Create a key
Calls are authenticated with an API key, not your password. Open Account, then the API Keys tab, and click New Key.

A key needs three things.
- A name, so you can tell keys apart later. Name it after the job it does, like
ci-deployorterraform. - Its scopes, the set of actions it is allowed to take.
readis enough to list and view everything; each write area (servers, IPs, VMs, monitoring and so on) is a separate box you tick only if this key needs it. Picking a write scope includes reading, so aservers.writekey can also read servers. - Its allowed source IPs, one address or CIDR per line. This is required. The key works only from the addresses you list, so a leaked key is useless from anywhere else. Put your office range, your CI runner's egress address, or a server's IP here. Both IPv4 and IPv6 are accepted.

You can also set an expiry in days; leave it blank for a key that never expires. When you save, the key is shown once. Copy it then, because it is not stored in a form you can read back. If you lose it, revoke it and make a new one.

Make your first call
The key goes in an Authorization: Bearer header. Start with GET /v1/account, the whoami call: it needs only read, changes nothing, and tells you the key works.
curl -s https://api.amoni.app/v1/account \
-H "Authorization: Bearer nr_live_..."
The response is your account, and a key block that reflects the key you just used: its name, its scopes and the addresses it is locked to. It is the fastest way to confirm a key is set up the way you meant.
{
"success": true,
"data": {
"id": 108,
"email": "you@example.com",
"company": "Example Hosting B.V.",
"key": {
"name": "ci-deploy",
"prefix": "nr_live_25336a5b",
"scopes": ["read", "servers.write"],
"allowed_ips": ["198.51.100.0/24"],
"expires_at": "2027-09-22 14:23:59",
"last_used_at": "2026-09-22T14:24:10+00:00"
}
}
}
From here, GET /v1/servers lists your servers, GET /v1/ips your addresses, GET /v1/vms your cloud instances. Every response uses the same envelope: { "success": true, "data": ... } on success, and { "success": false, "error": "...", "code": "..." } on failure, so your code can branch on success and read code for the machine-readable reason.
What to expect
- Rate limit. A key may make 120 requests a minute. Over that, calls answer
429with aRetry-Afterheader saying how long to wait. Space out bulk work rather than firing it all at once. - Wrong scope. A call your key is not scoped for answers
403with codeinsufficient_scope, and the message names the scope to add. See API key scopes and least privilege. - Wrong address. A call from an address not on the key's allow-list answers
403with codeip_not_allowed, and the message names the address it saw. Add that address to the key, or call from one already listed. - Bad or expired key. An unknown or malformed key answers
401; an expired one answers401with codeexpired_api_keyand the date it lapsed.
The full reference
Every route, its scope and its request and response shape live in the OpenAPI specification, served at https://api.amoni.app/v1/openapi.yaml with browsable docs at https://api.amoni.app/v1/docs. Point your own generator at the spec to get a typed client in your language.
Questions we get
- Can a key create other keys? No. Keys are made in the portal only, by a signed-in person. A leaked key cannot mint more access.
- How many keys can I have? Ten per account. That is room for one per job, which is the point.
- Do I have to set allowed IPs? Yes, on every key. If your caller's address is dynamic, list its range or your provider's egress block rather than a single address.
- Is this the same API resellers use? Yes. The same surface backs the Terraform provider and reseller integrations; scopes and the allow-list are what keep each key to its job.
Still stuck?
Open a support ticket with the key name and the code from the error you got back. We can see, on our side, exactly why a call was refused.