Mint an Ephemeral Key
curl --request POST \
--url https://api.covalenthq.com/platform/ephemeral_keys/ \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"ttl_seconds": 123
}
'import requests
url = "https://api.covalenthq.com/platform/ephemeral_keys/"
payload = { "ttl_seconds": 123 }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({ttl_seconds: 123})
};
fetch('https://api.covalenthq.com/platform/ephemeral_keys/', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.covalenthq.com/platform/ephemeral_keys/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'ttl_seconds' => 123
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.covalenthq.com/platform/ephemeral_keys/"
payload := strings.NewReader("{\n \"ttl_seconds\": 123\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.covalenthq.com/platform/ephemeral_keys/")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"ttl_seconds\": 123\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.covalenthq.com/platform/ephemeral_keys/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"ttl_seconds\": 123\n}"
response = http.request(request)
puts response.read_body{
"ephemeral_key": "<string>",
"expires_at": "<string>"
}Ephemeral Keys
Mint an Ephemeral Key
Mint a short-lived, safe-to-embed Ephemeral Key (gr_ek_ RS256 JWT) from a parent API key, for use in browsers, mobile apps, and WebSocket clients.
POST
/
platform
/
ephemeral_keys
/
Mint an Ephemeral Key
curl --request POST \
--url https://api.covalenthq.com/platform/ephemeral_keys/ \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"ttl_seconds": 123
}
'import requests
url = "https://api.covalenthq.com/platform/ephemeral_keys/"
payload = { "ttl_seconds": 123 }
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({ttl_seconds: 123})
};
fetch('https://api.covalenthq.com/platform/ephemeral_keys/', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.covalenthq.com/platform/ephemeral_keys/",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'ttl_seconds' => 123
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.covalenthq.com/platform/ephemeral_keys/"
payload := strings.NewReader("{\n \"ttl_seconds\": 123\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.covalenthq.com/platform/ephemeral_keys/")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"ttl_seconds\": 123\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.covalenthq.com/platform/ephemeral_keys/")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"ttl_seconds\": 123\n}"
response = http.request(request)
puts response.read_body{
"ephemeral_key": "<string>",
"expires_at": "<string>"
}Credential
Parent API key (
cqt_… / ckey_…)Processing
Realtime
gr_ek_, that you can safely hand to an untrusted client (browser, mobile app, WebSocket). The token carries no secret and expires on its own, so a stolen token is only useful until its exp. See the Ephemeral Keys guide for the full security model and recipes.
Authenticate with your parent API key, not with an Ephemeral Key. An Ephemeral Key cannot mint another Ephemeral Key, and a gr_sk_… service key cannot mint either.
Platform endpoints on
api.covalenthq.com require a trailing slash - call /platform/ephemeral_keys/, not /platform/ephemeral_keys.Endpoint
POST https://api.covalenthq.com/platform/ephemeral_keys/
Authorization: Bearer <GOLDRUSH_API_KEY>
Request
The request body is optional JSON. In this release the only accepted field isttl_seconds; any other fields are ignored, and an Ephemeral Key always inherits the parent’s full access.
integer
default:"1800"
Requested lifetime in seconds. Defaults to 1800 (30 min). Clamped to
[1, 3600] - the server maximum is 3600 (60 min), so larger requests are silently reduced to the cap. A non-integer value returns 400.Example
curl -X POST "https://api.covalenthq.com/platform/ephemeral_keys/" \
-H "Authorization: Bearer $GOLDRUSH_API_KEY" \
-H "Content-Type: application/json" \
-d '{"ttl_seconds": 1800}'
const response = await fetch(
"https://api.covalenthq.com/platform/ephemeral_keys/",
{
method: "POST",
headers: {
Authorization: `Bearer ${process.env.GOLDRUSH_API_KEY}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ ttl_seconds: 1800 }),
},
);
const { ephemeral_key, expires_at } = await response.json();
import os, requests
response = requests.post(
"https://api.covalenthq.com/platform/ephemeral_keys/",
json={"ttl_seconds": 1800},
headers={"Authorization": f"Bearer {os.environ['GOLDRUSH_API_KEY']}"},
)
response.raise_for_status()
body = response.json()
Response
200 OK
{
"ephemeral_key": "gr_ek_eyJhbGciOiJSUzI1NiIsImtpZCI6…",
"expires_at": "2026-08-03T18:30:00+00:00"
}
Field descriptions
string
The signed RS256 JWT, prefixed
gr_ek_. This is what you hand to the client. It carries no secret - it is trusted only because of its signature.string
ISO-8601 UTC timestamp at which the token’s
exp claim falls due. Refresh before this time.Token claims
An Ephemeral Key is a standard RS256 JWT. Any verifier can decode it (no private key needed) and validate its signature against the Ephemeral JWKS endpoint.| Claim | Meaning |
|---|---|
iss | Always goldrush. |
sub | Unique id of this Ephemeral Key (a per-mint session id, e.g. ek_…). |
parent_key_id | The minting API key’s non-secret public id (a UUID) - usage, quota, and billing attribute here. The raw cqt_/ckey_ secret never appears in the token. |
iat | Issued-at (UNIX seconds). |
exp | Expiry (UNIX seconds). Verifiers reject the token after this. |
kid) travels in the JWT header, not the claims. There is no scopes claim in this release.
Use the Ephemeral Key
Hand the returnedgr_ek_… token to your client and use it exactly where you would use an API key - as a Bearer token, Basic Auth username, or key query parameter - against the data APIs. It authenticates on behalf of the parent API key.
# Bearer token
curl -X GET "https://api.covalenthq.com/v1/eth-mainnet/address/demo.eth/balances_v2/" \
-H "Authorization: Bearer $GOLDRUSH_EPHEMERAL_KEY"
# or as a query parameter
curl -X GET "https://api.covalenthq.com/v1/eth-mainnet/address/demo.eth/balances_v2/?key=$GOLDRUSH_EPHEMERAL_KEY"
# Hyperliquid WebSocket takes the credential as a query parameter at connect time
wscat -c "wss://hypercore.goldrushdata.com/ws?key=$GOLDRUSH_EPHEMERAL_KEY"
Common uses
- Browser / mobile embedding - mint on your backend, hand the
gr_ek_…token to the client so it can call the data APIs directly without exposing yourcqt_…key. - WebSocket auth - web-locked keys don’t cover WebSockets; an Ephemeral Key does. See Recipe B.
- Per-session isolation - one token per client session, refreshed on a timer, so any single stolen token is short-lived.
Common errors
Errors are returned in the standard GoldRush envelope ({ "error": true, "error_message": …, "error_code": … }).
| Status | Cause | Fix |
|---|---|---|
400 Bad Request | Body isn’t valid JSON, or ttl_seconds isn’t an integer. | Send valid JSON with an integer ttl_seconds (or omit the body). |
401 Unauthorized | Missing/malformed Authorization header, or the bearer isn’t a valid parent API key. | Confirm Authorization: Bearer <cqt_…> with an active API key. |
403 Forbidden | The bearer is a gr_ek_… Ephemeral Key or a gr_sk_… service key (neither may mint), or the parent API key isn’t enabled for Ephemeral Keys. | Authenticate with a parent API key enabled for Ephemeral Keys. |
404 Not Found | Ephemeral Keys aren’t enabled on this deployment. | Contact support to enable the feature. |
429 Too Many Requests | The parent API key exceeded its mint rate (default 60/min). | Mint one token per session and refresh on a timer; back off and retry. |
This endpoint is rate-limited per parent API key (default 60 mints/minute). Mint one Ephemeral Key per client session and refresh on a timer - do not mint a fresh token on every request.