Skip to content
KeylayerDocs
Docs menu
PricingGet started

Use Replicate with a scoped key and a request cap

Set the Replicate client's base URL to https://relay.keylayer.dev, or https://relay.keylayer.dev/v1 for the JavaScript client, and use your Keylayer key as the API token; Keylayer forwards each call to api.replicate.com. Replicate keys take request caps, a per-minute rate and a concurrency limit but no spend cap, and a request can run for up to 120 seconds, which covers Replicate's 60-second default wait.

Set up

Replicate tokens can't be scoped, so add a token that only Keylayer uses and that you can revoke on its own. Then mint a key as in the quickstart and put it in an environment variable. The examples use replicate 1.0.7 for Python and 1.4.0 for Node.js.

Shell
export KEYLAYER_KEY=kl_k_YOUR_KEY
Python (replicate)
import os

import replicate

client = replicate.Client(
    api_token=os.environ["KEYLAYER_KEY"],
    base_url="https://relay.keylayer.dev",
)

output = client.run(
    "black-forest-labs/flux-schnell",
    input={"prompt": "a lighthouse on a cliff at dawn"},
)
print(output)
Node.js (replicate)
import Replicate from "replicate";

const replicate = new Replicate({
  auth: process.env.KEYLAYER_KEY,
  baseUrl: "https://relay.keylayer.dev/v1",
});

const output = await replicate.run("black-forest-labs/flux-schnell", {
  input: { prompt: "a lighthouse on a cliff at dawn" },
});
console.log(output);
curl
curl https://relay.keylayer.dev/v1/models/black-forest-labs/flux-schnell/predictions \
  -H "Authorization: Bearer $KEYLAYER_KEY" \
  -H "Content-Type: application/json" \
  -H "Prefer: wait" \
  -d '{"input": {"prompt": "a lighthouse on a cliff at dawn"}}'

The Python client adds /v1 itself; the JavaScript client expects it in baseUrl. Both send Prefer: wait, so Replicate holds the request open for up to 60 seconds before the client starts polling.

Request caps on Replicate

Keylayer has no price table for Replicate, so a Replicate key can't have a spend cap and minting one with a spend cap is refused. Limit it with Requests, Requests a minute and Requests at once; see request caps and rate.

Every poll is a request. After the wait, both clients poll GET /v1/predictions/{id} every 0.5 seconds, up to 120 requests a minute for one prediction, and each poll counts against the key's caps. In Python, set REPLICATE_POLL_INTERVAL to poll less often.

Warning: The Python client retries a GET that gets 429, up to 10 tries in all, and sleeps for the full retry-after between tries. A poll refused with quota_exceeded carries the time until the cap resets, so run() can stall for hours. Leave room for polling in the request cap.

Blocked routes

Keylayer refuses these routes with 403 path_not_allowed:

Route What it does
/v1/deployments Lists and creates deployments
/v1/deployments/{owner}/{name} Reads, changes and deletes a deployment
/v1/account Returns your account
/v1/webhooks/** Returns your webhook signing secret

A deployment keeps its hardware running after the key that created it is revoked, so managing deployments is blocked. Running predictions on a deployment you already have, with POST /v1/deployments/{owner}/{name}/predictions, works. Escape hatches lift the block for one key; see admin routes and escape hatches.

Errors

A Keylayer refusal has a keylayer-error header and a Replicate-shaped body:

403 path_not_allowed
{"title": "path_not_allowed", "detail": "Keylayer path_not_allowed: this key does not allow this path.", "status": 403}

The Python client raises ReplicateError with status, title and detail; title holds the code, except on flat refusals such as invalid_token, where only status is set. The JavaScript client throws an error with the fetch Response attached:

Node.js (replicate)
import Replicate from "replicate";

const replicate = new Replicate({
  auth: process.env.KEYLAYER_KEY,
  baseUrl: "https://relay.keylayer.dev/v1",
});

try {
  await replicate.run("black-forest-labs/flux-schnell", {
    input: { prompt: "a lighthouse on a cliff at dawn" },
  });
} catch (error) {
  const response =
    error instanceof Error && "response" in error ? error.response : null;
  if (!(response instanceof Response)) {
    throw error;
  }
  const code = response.headers.get("keylayer-error");
  if (!code) {
    throw error;
  }
  console.log(`Keylayer refused the request: ${response.status} ${code}`);
}

The error reference lists every code.

What doesn't work

  • Streams and output files come from stream.replicate.com and replicate.delivery URLs inside the response. Keylayer doesn't rewrite URLs inside response bodies, so those downloads go straight to Replicate, outside the key's limits.
  • The Python client sends its Authorization header, which holds your Keylayer key, to those hosts too. Pass use_file_output=False to run() to get plain URLs and download them without it.
  • Managing deployments, and spend caps, as above.

Next steps

Updated

Create your first scoped key

Add a provider key once, then mint Keylayer keys with their own limits for each app, agent or teammate.

Get started