REST APIHow-tos

Log in

Get a JWT for a wallet with two requests, and send it with the wallet's own requests.

Endpoints that act for a wallet, such as creating, reading and removing its orders, need a JWT for that wallet. You get one by signing a message with the wallet's key. This page shows the requests for a regular wallet (an EOA). For a Predict account, the message is signed through the SDK instead: see Authentication.

1. Get the message

GET /v1/auth/message returns the message to sign. It needs only your API key.

{
  "success": true,
  "data": {
    "message": "Please sign this message to log in. By signing, you accept the Predict Terms of Service, which you can find here: https://docs.predict.fun/terms-of-service Timestamp: 1793832395205"
  }
}

The message ends with the time it was issued. Sign and send it within 10 minutes.

2. Sign it

Sign the message exactly as you received it, as a personal message (EIP-191). This is the standard personal_sign that every Ethereum library and wallet supports.

3. Get the JWT

Send the wallet's address, the message and the signature to POST /v1/auth:

{
  "signer": "0xYourWalletAddress",
  "message": "Please sign this message to log in. …",
  "signature": "0x…"
}

The response holds the JWT in data.token:

{
  "success": true,
  "data": { "token": "eyJ0eXAiOiJKV1Qi…" }
}

4. Use it

Send the JWT in the Authorization header, as Bearer <jwt>, next to your API key. A JWT is valid for 24 hours. After that, log in again.

The whole flow

This logs in, then lists the wallet's open orders with the JWT:

import { Wallet } from "ethers";

const apiKey = "YOUR_API_KEY";
const apiUrl = "https://api.predict.fun";
const wallet = new Wallet(process.env.WALLET_PRIVATE_KEY!);

// 1. Get the message to sign.
const messageResponse = await fetch(`${apiUrl}/v1/auth/message`, {
  headers: { "x-api-key": apiKey },
});
const { message } = (await messageResponse.json()).data;

// 2. Sign it as a personal message (EIP-191).
const signature = await wallet.signMessage(message);

// 3. Exchange the signature for a JWT.
const loginResponse = await fetch(`${apiUrl}/v1/auth`, {
  method: "POST",
  headers: { "Content-Type": "application/json", "x-api-key": apiKey },
  body: JSON.stringify({ signer: wallet.address, message, signature }),
});
const login = await loginResponse.json();
if (!loginResponse.ok) throw new Error(`${login.error}: ${login.message}`);
const jwt = login.data.token;

// 4. Send the JWT with the API key on the wallet's own requests.
const ordersResponse = await fetch(`${apiUrl}/v1/orders?status=OPEN`, {
  headers: { "x-api-key": apiKey, Authorization: `Bearer ${jwt}` },
});
const orders = await ordersResponse.json();
console.log(`${orders.data.length} open orders`);

When it fails

A rejected login is a 401 with error: "unauthorized". The message says why:

messageCauseFix
invalid signatureThe signature doesn't match the message and signer.Sign the message exactly as you received it, with the key of signer.
invalid messageThe message isn't the one the API sent.Send the message back unchanged.
invalid message timestampThe message was issued more than 10 minutes ago, or its timestamp was changed.Get a new message and sign it right away.
authorization errorThe API key is missing or invalid, or a later request has no JWT or an expired one.Check the API key. Log in again, and send the JWT as Bearer <jwt>.

A body that can't be read, such as one with a malformed signer address, gets a 400 instead.

On this page