> ## Documentation Index
> Fetch the complete documentation index at: https://docs.ravn.exchange/llms.txt
> Use this file to discover all available pages before exploring further.

# Get an API key

> Instant, no review. 4x your rate limit and put your project on the map.

export const ApiKeyForm = () => {
  const API_URL = "https://app.ravn.exchange/api/v1/keys";
  const CATEGORIES = ["Wallet", "DApp", "Aggregator", "Wallet infra", "Other"];
  const CHAINS = ["Ethereum", "Optimism", "BNB Chain", "Unichain", "Polygon", "zkSync", "World Chain", "HyperEVM", "Base", "Arbitrum", "Linea", "Avalanche", "Robinhood", "Monad", "Bitcoin", "Solana"];
  const APIS = ["quote", "execute", "status", "submit-signature", "tokens/resolve", "tokens/btc-coverage"];
  const inputStyle = {
    width: "100%",
    padding: "0.5rem 0.75rem",
    borderRadius: "0.375rem",
    border: "1px solid var(--gray-a6, #d1d5db)",
    fontSize: "0.9375rem",
    boxSizing: "border-box"
  };
  function renderField(label, required, children) {
    return <div style={{
      marginBottom: "1rem"
    }}>
        <label style={{
      display: "block",
      fontSize: "0.875rem",
      fontWeight: 600,
      marginBottom: "0.25rem"
    }}>
          {label}{required ? " *" : ""}
        </label>
        {children}
      </div>;
  }
  const [form, setForm] = useState({
    email: "",
    projectName: "",
    website: "",
    telegram: "",
    twitter: "",
    discord: "",
    blurb: "",
    category: "",
    chains: [],
    apisPlanned: []
  });
  const [status, setStatus] = useState("idle");
  const [result, setResult] = useState(null);
  const [errorMessage, setErrorMessage] = useState("");
  const [copied, setCopied] = useState(false);
  function update(field, value) {
    setForm(f => ({
      ...f,
      [field]: value
    }));
  }
  function toggleMulti(field, value) {
    setForm(f => {
      const set = new Set(f[field]);
      if (set.has(value)) set.delete(value); else set.add(value);
      return {
        ...f,
        [field]: Array.from(set)
      };
    });
  }
  async function handleSubmit(e) {
    e.preventDefault();
    setStatus("submitting");
    setErrorMessage("");
    try {
      const res = await fetch(API_URL, {
        method: "POST",
        headers: {
          "content-type": "application/json"
        },
        body: JSON.stringify(form)
      });
      const body = await res.json();
      if (!res.ok) {
        const fieldErrors = body?.error?.details?.fieldErrors || ({});
        const fields = Object.keys(fieldErrors);
        const detail = fields.length ? fields.map(f => `${f}: ${fieldErrors[f][0]}`).join(", ") : null;
        setErrorMessage(detail || body?.error?.message || "Something went wrong. Please try again.");
        setStatus("error");
        return;
      }
      setResult(body.data);
      setStatus("done");
    } catch {
      setErrorMessage("Couldn't reach the API. Please try again in a moment.");
      setStatus("error");
    }
  }
  if (status === "done" && result) {
    return <div style={{
      border: "1px solid rgba(128,128,128,0.35)",
      borderRadius: "0.5rem",
      padding: "1.25rem"
    }}>
        <p style={{
      fontWeight: 600,
      marginTop: 0
    }}>Your API key. It's shown once, so save it now.</p>
        <div style={{
      display: "flex",
      gap: "0.5rem",
      alignItems: "center"
    }}>
          <code style={{
      flex: 1,
      padding: "0.5rem 0.75rem",
      background: "rgba(128,128,128,0.15)",
      borderRadius: "0.375rem",
      overflowX: "auto",
      whiteSpace: "nowrap"
    }}>
            {result.apiKey}
          </code>
          <button type="button" onClick={() => {
      navigator.clipboard.writeText(result.apiKey);
      setCopied(true);
      setTimeout(() => setCopied(false), 2000);
    }} style={{
      padding: "0.5rem 0.75rem",
      borderRadius: "0.375rem",
      border: "1px solid rgba(128,128,128,0.35)",
      cursor: "pointer"
    }}>
            {copied ? "Copied" : "Copy"}
          </button>
        </div>
        <p style={{
      fontSize: "0.875rem",
      opacity: 0.75
    }}>
          Partner ID: <code>{result.partnerId}</code>. This key can't be retrieved later. If
          you lose it, generate a new one.
        </p>
      </div>;
  }
  return <form onSubmit={handleSubmit}>
      {renderField("Email", true, <input style={inputStyle} type="email" required value={form.email} onChange={e => update("email", e.target.value)} />)}
      {renderField("Project name", true, <input style={inputStyle} type="text" required value={form.projectName} onChange={e => update("projectName", e.target.value)} />)}
      {renderField("Project website", true, <input style={inputStyle} type="url" required placeholder="https://…" value={form.website} onChange={e => update("website", e.target.value)} />)}
      {renderField("Telegram handle", true, <input style={inputStyle} type="text" required placeholder="@yourhandle" value={form.telegram} onChange={e => update("telegram", e.target.value)} />)}
      {renderField("Project Twitter handle", true, <input style={inputStyle} type="text" required placeholder="@yourproject" value={form.twitter} onChange={e => update("twitter", e.target.value)} />)}
      {renderField("Discord handle", false, <input style={inputStyle} type="text" value={form.discord} onChange={e => update("discord", e.target.value)} />)}
      {renderField("Project blurb", false, <textarea style={{
    ...inputStyle,
    minHeight: "4rem"
  }} value={form.blurb} onChange={e => update("blurb", e.target.value)} />)}
      {renderField("Category", false, <select style={inputStyle} value={form.category} onChange={e => update("category", e.target.value)}>
          <option value="">Select one…</option>
          {CATEGORIES.map(c => <option key={c} value={c}>{c}</option>)}
        </select>)}
      {renderField("Which chains do you plan to use?", false, <div style={{
    display: "flex",
    flexWrap: "wrap",
    gap: "0.5rem"
  }}>
          {CHAINS.map(c => <label key={c} style={{
    display: "flex",
    alignItems: "center",
    gap: "0.25rem",
    fontSize: "0.875rem"
  }}>
              <input type="checkbox" checked={form.chains.includes(c)} onChange={() => toggleMulti("chains", c)} />
              {c}
            </label>)}
        </div>)}
      {renderField("Which APIs do you plan to use?", false, <div style={{
    display: "flex",
    flexWrap: "wrap",
    gap: "0.5rem"
  }}>
          {APIS.map(a => <label key={a} style={{
    display: "flex",
    alignItems: "center",
    gap: "0.25rem",
    fontSize: "0.875rem"
  }}>
              <input type="checkbox" checked={form.apisPlanned.includes(a)} onChange={() => toggleMulti("apisPlanned", a)} />
              <code>{a}</code>
            </label>)}
        </div>)}

      {status === "error" && <p style={{
    color: "#dc2626",
    fontSize: "0.875rem"
  }}>{errorMessage}</p>}

      <button type="submit" disabled={status === "submitting"} style={{
    padding: "0.625rem 1.25rem",
    borderRadius: "0.375rem",
    border: "none",
    background: "rgb(var(--primary, 200 135 63))",
    color: "white",
    fontWeight: 600,
    cursor: status === "submitting" ? "default" : "pointer",
    opacity: status === "submitting" ? 0.7 : 1
  }}>
        {status === "submitting" ? "Generating…" : "Get my API key"}
      </button>
    </form>;
};

Takes 30 seconds. Fill in the form, get your key immediately, no waiting on a human, no
approval queue. You go from 30 requests/min to 120, and your project shows up by name in
RAVN's usage tracking, which is step one toward a partnership or a rev-share deal down the line.

<ApiKeyForm />

<Note>
  Not ready yet? You can still call the API with zero setup: every endpoint works with no
  `x-api-key` header at all, capped at 30 requests/min per IP. Come back here whenever you want
  to scale up.
</Note>

<Tip>
  Building something serious and want a custom rate limit or a dedicated fee arrangement? Reach
  out to the RAVN team directly instead, covered in [Authentication](/authentication#enterprise-partnerships).
</Tip>
