one command, and your port has a public URL

The API you already run, reachable from outside.

ApiArk puts a gateway in front of it and gives it a public HTTPS address, with a key that has to be presented on every call and a log of every call that gets through. Your backend code stays as it is.

Free plan: up to 10 APIs, 1,000 requests a day per key, no card. Open beta.

one tunnel, then one call through the chain
$ apigateway --token ark_9f3d… --port 3000
tunnel up https://api.apiark.io/fox
key auth on
rate limit 1000 req/day per key
request log on
$ curl https://api.apiark.io/fox/orders -H "apikey: ark_…"
200 OK · 41ms
logs / project: foxlast 5 requests
Five requests through the gateway. One failed authentication with 401 and one exceeded the daily count with 429, both stopped at the edge.
methodpathstatus
GET/fox/orders200
POST/fox/orders201
GET/fox/customers200
POST/fox/webhooks401
GET/fox/orders429

The 401 and 429 never reached the backend. They cost you no bandwidth and no compute.

What sits between the caller and your API

Four checks, on every route, in the same order every time. None of them is something you switch on, and none of them is something your code has to know about.

route / fox-orderschecks: 4
GET /fox/orderskey checkrate limitrequest logyour API

Every route runs these checks, whether the caller is a person with a key or an agent with a token. You configure nothing to turn them on.

key check
A caller without a valid key never reaches your backend. Keys are issued and revoked from the dashboard.
rate limit
Counted at the gateway, per key. A leaked key cannot spend anyone else’s budget.
request log
Method, path, status and latency for every call, in the dashboard.
scope check
A key reaches only what you granted it. Anything else is refused right here, with a 403.

Expose an API, in three steps

The path most people arrive on, and the one that replaces a tunnel plus whatever you were going to bolt onto it once the URL was public.

  1. 1

    Get the CLI

    One binary, macOS and Linux. Sign in and the dashboard gives you the download and your token.

  2. 2

    Point it at your port

    You get a public HTTPS URL with key auth, rate limits and logging already on. An API already on a server is added from the dashboard instead, with no CLI.

  3. 3

    Hand out keys, watch traffic

    Callers authenticate with their own key. You see every request in the dashboard as it arrives.

terminalafter the download
$ apigateway --token ark_9f3d… --port 3000
$ curl https://api.apiark.io/fox/orders -H "apikey: ark_…"
routes / foxcreated
public url
https://api.apiark.io/fox
upstream
tunnel → localhost:3000
key auth
on
rate limit
1,000 / day, per key
request log
on, streaming
backend changes
none

And when the caller is an AI agent

  1. 1

    Point ApiArk at your API

    Import an OpenAPI document in JSON and every operation becomes a tool, or type an operation by hand when the app serves no spec, which is most local Express and Fastify apps.

  2. 2

    Tick the operations the agent may call

    Read three, leave the writes alone. The selection is what the gateway enforces, not a hint the agent is asked to respect.

  3. 3

    Paste one line into the client

    You get an MCP URL and a bearer token. The token is shown once and stored only as a hash, so nobody can read it back, including us.

Clients that take a bearer token in a header:

  • Claude Code
  • Claude Desktop
  • Cursor
  • Pi
agents / new / capabilities3 of 6 selected

Three of six operations ticked. The agent gets listOrders, getOrder and searchOrders. Everything else answers 403.

This is the whole of the permission model. What you tick becomes the agent’s tool list, and the routes behind those operations are the only ones its token opens.

What the agent can and cannot do

This is the part a hand written MCP server does not give you, and the reason to use something that sits on the gateway instead of beside it.

The agent is its own identity on the gateway
Not a copy of your key with a label on it. It has its own consumer, its own quota and its own rows in the log.
An operation you did not tick answers 403
The refusal happens at the gateway, before your backend is involved. It does not depend on the agent choosing well.
Deleting the agent revokes it immediately
The consumer comes off the whitelist in the same action. There is no window where an old token still works.
The token is stored only as a hash
It is shown once at creation. Lose it and you delete the agent and make another, because nobody can read it back.
agents / orders-roactive
name
orders-ro
consumer
fox_orders_ro_a41c9e2b7f03
token
sha256:9c4a…e10f
operations
3 of 6
daily count
412 of 1,000
created
2026-08-25 09:14

Deleting the agent unwhitelists its consumer in the same action.

logs / agent: orders-rolast 4 calls
Four calls an agent made through the gateway. The last one, deleteOrder, was not among the operations granted to it and came back 403.
agenttoolstatus
orders-rolistOrders200
orders-rogetOrder200
orders-rosearchOrders200
orders-rodeleteOrder403

deleteOrder was never ticked, so the gateway refused it. Not the agent’s restraint, the gateway’s.
an illustration: Request Logs shows the call and its 403 today, without the agent and tool columns

The token column is a hash because that is all we keep. The count is the agent’s own, so a busy agent cannot spend the budget of the one next to it.

Why not write the MCP server yourself

You can, and for one tool on your own laptop you probably should. A hand written server is twenty lines until it needs somewhere to run, a token per agent, a quota, an audit trail and a way to revoke one agent without touching the others. That is the part ApiArk already has, because it is the same gateway your API is behind.

What it works with

Any HTTP API. Nothing is installed in your backend and no library is imported, so the framework only matters to you.

Node.js
Next.js
Python / FastAPI
Go / gRPC
Laravel
PHP
Rust / Actix
+ Any HTTP/REST

How much work each one is

Every tool here can get you to the same place. The difference is how many steps, config files and moving parts it takes. This is what each one asks of you, not a claim that the others cannot do it.

Give an agent scoped access to your API

ngrok
Bring your own MCP server, then a policy file in front
Cloudflare Tunnel
openApiMcpServer in a Worker, deploy it
Kong / Envoy
AI MCP Proxy plugin, declare the tools in YAML
ApiArk
Tick the operations, copy one line

Public HTTPS URL for a local port

ngrok
Sign up, add an authtoken, one command
Cloudflare Tunnel
One command, or a named tunnel for a real hostname
Kong / Envoy
Not its job, bring your own tunnel
ApiArk
One command

Add API key auth on top

ngrok
Edit a traffic policy file, paid plans
Cloudflare Tunnel
Set up Access, issue service tokens
Kong / Envoy
Declare a consumer and wire up an auth plugin
ApiArk
On by default, key issued when you publish

Per-key rate limit

ngrok
Policy rule, paid plans
Cloudflare Tunnel
IP only below Enterprise; per-key needs Advanced Rate Limiting
Kong / Envoy
Plugin config, Redis for multi-node
ApiArk
On by default, counted per key

Config files to write

ngrok
One policy file for auth or limits
Cloudflare Tunnel
None for a quick tunnel; Access rules in the dashboard
Kong / Envoy
Declarative YAML; none on Konnect serverless
ApiArk
None

Compared against each vendor’s own documentation, checked 27 August 2026. If something here is out of date, tell us at [email protected] and we will correct it.

Questions people ask first

Do I have to write an MCP server?
No. Point ApiArk at an OpenAPI document in JSON and each operation becomes a tool, or type an operation by hand when the app serves no spec, which is most local Express and Fastify apps.
Does my API have to be public?
No. Run the CLI against a local port and the gateway reaches it through the tunnel. An API you already deployed is added from the dashboard with no CLI.
Which agent clients work?
Claude Code, Claude Desktop, Cursor and Pi, through one line of header configuration. claude.ai on the web and ChatGPT need OAuth, which is not built.
Can the agent call something I did not grant?
No. Each agent is a separate identity on the gateway and is whitelisted only on the routes behind the operations you ticked. Anything else returns 403.
What happens to my token?
Only its hash is stored. It is shown once at creation. Lose it and you delete the agent and make another.
Which MCP revision?
Both. The stateless flow from revision 2026-07-28 and the older initialize handshake, chosen per request, so a client on either side of the change works.
Does my code change?
No. ApiArk sits in front of the API and forwards to it.
What happens at 1,000 requests?
The gateway returns 429 for the rest of the day, per key, so one noisy agent does not spend another one’s budget.
Who runs the gateway?
We do. You do not operate a gateway, you do not host anything, and you write no configuration files.
Do you inspect or store request bodies?
No. The gateway proxies over TLS and writes one metadata line per request: method, path, status code, latency and request headers. Bodies are not captured, not inspected and not written to disk.
How much latency does the gateway add?
The auth check and the rate limit counter add a few milliseconds. What dominates the round trip is distance: the gateway runs in Switzerland, so a caller in Europe sees far less overhead than one in Asia or on the US west coast. Every request shows its measured latency in the live log, so you can check the real number for your own setup instead of trusting ours.

The API you already run, reachable from outside.

Free plan: up to 10 APIs, 1,000 requests a day per key, no card. Open beta.