> For the complete documentation index, see [llms.txt](https://sodex.com/documentation/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://sodex.com/documentation/trading-api/rest-v1/sodex-rest-public-api.md).

# Public REST API

Public REST endpoints use `${PUBLIC_ENDPOINT}` as the base URL:

* Mainnet: `https://mainnet-gw.sodex.dev/api/v1`
* Testnet: `https://testnet-gw.sodex.dev/api/v1`

## User

### Query API Keys

`GET ${PUBLIC_ENDPOINT}/user/{userAddress}/api-keys`

*Get API keys for the user's primary account by default, or an owned subaccount selected by `accountID`.*

```bash
curl -X GET "${PUBLIC_ENDPOINT}/user/0x0123456789070ce8f0d6bab722103d12674bc257/api-keys?accountID=12345" \
  -H 'Accept: application/json'
```

#### Path Params

| Name          | Type        | Required | Restrictions      | Description      |
| ------------- | ----------- | -------- | ----------------- | ---------------- |
| `userAddress` | `HexString` | `true`   | valid EVM address | User EVM address |

#### Query Params

| Name        | Type     | Required | Restrictions                         | Description                                                                      |
| ----------- | -------- | -------- | ------------------------------------ | -------------------------------------------------------------------------------- |
| `accountID` | `uint64` | `false`  | none                                 | Optional primary or owned subaccount ID. The primary account is used by default. |
| `name`      | `string` | `false`  | should match `^[0-9a-zA-Z_-]{1,36}$` | Optional API key name filter                                                     |

#### Response

| Name        | Type             | Required | Description                                                                                                            |
| ----------- | ---------------- | -------- | ---------------------------------------------------------------------------------------------------------------------- |
| `code`      | `int32`          | `true`   | Response status code. `0` means success.                                                                               |
| `data`      | `AccountAPIKeys` | `true`   | Response data when `code` is `0`. See [`AccountAPIKeys`](/documentation/trading-api/rest-v1/schema.md#accountapikeys). |
| `timestamp` | `uint64`         | `true`   | Response timestamp in milliseconds.                                                                                    |

### Add API Key

`POST ${PUBLIC_ENDPOINT}/user/{userAddress}/api-keys`

*Add an API key to both spot and perps. Use `permissions` to disable selected API key permissions, or `builder` to approve a builder fee in the same signed action.*

* Auth: signed write with the universal domain; `X-API-Chain` is required

```bash
curl -X POST "${PUBLIC_ENDPOINT}/user/0x0123456789070ce8f0d6bab722103d12674bc257/api-keys" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'X-API-Chain: <API-Chain>' \
  -H 'X-API-Sign: <API-Sign>' \
  -H 'X-API-Nonce: <API-Nonce>' \
  -d <AddAPIKeyRequest | AddPermissionedAPIKeyRequest>
```

#### Path Params

| Name          | Type        | Required | Restrictions      | Description      |
| ------------- | ----------- | -------- | ----------------- | ---------------- |
| `userAddress` | `HexString` | `true`   | valid EVM address | User EVM address |

#### Headers

| Name          | Type        | Required | Description                                                                                  |
| ------------- | ----------- | -------- | -------------------------------------------------------------------------------------------- |
| `X-API-Chain` | `uint64`    | `true`   | Any `uint64`. Use this exact value as EIP-712 `domain.chainId` when generating `X-API-Sign`. |
| `X-API-Sign`  | `HexString` | `true`   | EIP-712 universal signature from the user's wallet                                           |
| `X-API-Nonce` | `uint64`    | `true`   | Unique nonce in milliseconds                                                                 |

#### Request Body

See [`AddAPIKeyRequest`](/documentation/trading-api/rest-v1/schema.md#addapikeyrequest) or [`AddPermissionedAPIKeyRequest`](/documentation/trading-api/rest-v1/schema.md#addpermissionedapikeyrequest) in [Schema](/documentation/trading-api/rest-v1/schema.md).

Additional Information:

* `accountID` must be the primary account ID or one of the subaccount IDs belonging to `userAddress`.
* The request succeeds only when both spot and perps accept it.
* `builder`, if provided, uses [`BuilderParams`](/documentation/trading-api/rest-v1/schema.md#builderparams).
* `permissions`, if provided, is a bit mask where a set bit disables the corresponding [`APIKeyPermissionEnum`](/documentation/trading-api/rest-v1/schema.md#apikeypermissionenum).
* A permissioned API key must disable `TRADE`, `CANCEL`, or both; enabling both `TRADE` and `CANCEL` is not supported.
* `builder` and `permissions` cannot be used together in the same request.

#### Response

| Name        | Type     | Required | Description                              |
| ----------- | -------- | -------- | ---------------------------------------- |
| `code`      | `int32`  | `true`   | Response status code. `0` means success. |
| `data`      | `null`   | `true`   | No endpoint-specific response payload.   |
| `timestamp` | `uint64` | `true`   | Response timestamp in milliseconds.      |

### Revoke API Key

*API key revocation does not support the universal domain. To revoke an API key from both markets, submit two engine-specific requests.*

1. Call `DELETE ${SPOT_ENDPOINT}/accounts/api-keys` with a `spot` domain signature. See [Remove API Key in the Spot REST API](/documentation/trading-api/rest-v1/sodex-rest-spot-api.md#remove-api-key).
2. Call `DELETE ${PERPS_ENDPOINT}/accounts/api-keys` with a `futures` domain signature. See [Remove API Key in the Perps REST API](/documentation/trading-api/rest-v1/sodex-rest-perps-api.md#remove-api-key).

Use the same [`RevokeAPIKeyRequest`](/documentation/trading-api/rest-v1/schema.md#revokeapikeyrequest) body for both calls, but sign each request separately with the account's master wallet. Engine-specific EIP-712 signatures use signature type `0x01`; a universal `0x02` signature cannot be used for revocation. See [Trading actions](/documentation/trading-api/trading-api.md#trading-actions) for signing details and [REST API endpoints](/documentation/trading-api/rest-v1.md#endpoints) for the endpoint base URLs.

```bash
# Revoke from spot.
curl -X DELETE ${SPOT_ENDPOINT}/accounts/api-keys \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'X-API-Sign: <SPOT-API-Sign>' \
  -H 'X-API-Nonce: <SPOT-API-Nonce>' \
  -d <RevokeAPIKeyRequest>

# Revoke from perps.
curl -X DELETE ${PERPS_ENDPOINT}/accounts/api-keys \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'X-API-Sign: <PERPS-API-Sign>' \
  -H 'X-API-Nonce: <PERPS-API-Nonce>' \
  -d <RevokeAPIKeyRequest>
```

Both calls are required. A successful spot revoke does not revoke the key from perps, and a successful perps revoke does not revoke it from spot. Each call returns its own response and can succeed or fail independently.

### Query Builders

`GET ${PUBLIC_ENDPOINT}/user/{userAddress}/builders`

*Get approved builders for the user.*

```bash
curl -X GET "${PUBLIC_ENDPOINT}/user/0x0123456789070ce8f0d6bab722103d12674bc257/builders" \
  -H 'Accept: application/json'
```

#### Path Params

| Name          | Type        | Required | Restrictions      | Description      |
| ------------- | ----------- | -------- | ----------------- | ---------------- |
| `userAddress` | `HexString` | `true`   | valid EVM address | User EVM address |

#### Response

| Name        | Type       | Required | Description                                                                                                |
| ----------- | ---------- | -------- | ---------------------------------------------------------------------------------------------------------- |
| `code`      | `int32`    | `true`   | Response status code. `0` means success.                                                                   |
| `data`      | `Builders` | `true`   | Response data when `code` is `0`. See [`Builders`](/documentation/trading-api/rest-v1/schema.md#builders). |
| `timestamp` | `uint64`   | `true`   | Response timestamp in milliseconds.                                                                        |

### Approve Builder Fee

`POST ${PUBLIC_ENDPOINT}/user/{userAddress}/builders`

*Approve or clear a builder fee on both spot and perps.*

* Auth: signed write with the universal domain; `X-API-Chain` is required

```bash
curl -X POST "${PUBLIC_ENDPOINT}/user/0x0123456789070ce8f0d6bab722103d12674bc257/builders" \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json' \
  -H 'X-API-Chain: <API-Chain>' \
  -H 'X-API-Sign: <API-Sign>' \
  -H 'X-API-Nonce: <API-Nonce>' \
  -d <ApproveBuilderFeeRequest>
```

#### Path Params

| Name          | Type        | Required | Restrictions      | Description      |
| ------------- | ----------- | -------- | ----------------- | ---------------- |
| `userAddress` | `HexString` | `true`   | valid EVM address | User EVM address |

#### Headers

| Name          | Type        | Required | Description                                                                                  |
| ------------- | ----------- | -------- | -------------------------------------------------------------------------------------------- |
| `X-API-Chain` | `uint64`    | `true`   | Any `uint64`. Use this exact value as EIP-712 `domain.chainId` when generating `X-API-Sign`. |
| `X-API-Sign`  | `HexString` | `true`   | EIP-712 universal signature from the user's wallet                                           |
| `X-API-Nonce` | `uint64`    | `true`   | Unique nonce in milliseconds                                                                 |

#### Request Body

See [`ApproveBuilderFeeRequest`](/documentation/trading-api/rest-v1/schema.md#approvebuilderfeerequest) in [Schema](/documentation/trading-api/rest-v1/schema.md).

Additional Information:

* `accountID` must be the primary account ID of `userAddress`.
* `builderID` must exist as an account in both spot and perps and have at least `100` vUSDC in each engine.
* The request succeeds only when both spot and perps accept it.
* `maxFeeRate` must be between `0` and `2000`, inclusive.
* `maxFeeRate=0` clears the builder approval.

#### Response

| Name        | Type     | Required | Description                              |
| ----------- | -------- | -------- | ---------------------------------------- |
| `code`      | `int32`  | `true`   | Response status code. `0` means success. |
| `data`      | `null`   | `true`   | No endpoint-specific response payload.   |
| `timestamp` | `uint64` | `true`   | Response timestamp in milliseconds.      |

### Query user rate limits

`GET ${PUBLIC_ENDPOINT}/user/{userAddress}/ratelimit`

*Get transaction and cancel quota information for a user address.*

```bash
curl -X GET "${PUBLIC_ENDPOINT}/user/0x0123456789070ce8f0d6bab722103d12674bc257/ratelimit" \
  -H 'Accept: application/json'
```

#### Path Params

| Name          | Type        | Required | Restrictions      | Description      |
| ------------- | ----------- | -------- | ----------------- | ---------------- |
| `userAddress` | `HexString` | `true`   | valid EVM address | User EVM address |

#### Response

| Name        | Type            | Required | Description                                                                                                          |
| ----------- | --------------- | -------- | -------------------------------------------------------------------------------------------------------------------- |
| `code`      | `int32`         | `true`   | Response status code. `0` means success.                                                                             |
| `data`      | `UserRateLimit` | `true`   | Response data when `code` is `0`. See [`UserRateLimit`](/documentation/trading-api/rest-v1/schema.md#userratelimit). |
| `timestamp` | `uint64`        | `true`   | Response timestamp in milliseconds.                                                                                  |

## Announcement

> Mainnet only. These endpoints are not available on testnet.

### Query announcements

`GET ${PUBLIC_ENDPOINT}/announcements`

*Get a paginated list of public announcements.*

```bash
curl -X GET "${PUBLIC_ENDPOINT}/announcements?page=1&size=20&lang=en" \
  -H 'Accept: application/json'
```

#### Query Params

| Name   | Type     | Required | Restrictions               | Description                              |
| ------ | -------- | -------- | -------------------------- | ---------------------------------------- |
| `page` | `int`    | `false`  | `>= 1`; default `1`        | Page number                              |
| `size` | `int`    | `false`  | `1` to `100`; default `20` | Page size                                |
| `lang` | `string` | `false`  | `en`, `zh`, `ja`, `ko`     | Announcement language. Defaults to `en`. |

#### Response

| Name        | Type               | Required | Description                                                                                                                |
| ----------- | ------------------ | -------- | -------------------------------------------------------------------------------------------------------------------------- |
| `code`      | `int32`            | `true`   | Response status code. `0` means success.                                                                                   |
| `data`      | `AnnouncementList` | `true`   | Response data when `code` is `0`. See [`AnnouncementList`](/documentation/trading-api/rest-v1/schema.md#announcementlist). |
| `timestamp` | `uint64`           | `true`   | Response timestamp in milliseconds.                                                                                        |

### Query announcement detail

`GET ${PUBLIC_ENDPOINT}/announcements/detail/{id}`

*Get a public announcement detail by ID.*

```bash
curl -X GET "${PUBLIC_ENDPOINT}/announcements/detail/123?lang=en&plainText=false" \
  -H 'Accept: application/json'
```

#### Path Params

| Name | Type    | Required | Restrictions | Description     |
| ---- | ------- | -------- | ------------ | --------------- |
| `id` | `int64` | `true`   | numeric      | Announcement ID |

#### Query Params

| Name        | Type     | Required | Restrictions           | Description                                             |
| ----------- | -------- | -------- | ---------------------- | ------------------------------------------------------- |
| `lang`      | `string` | `false`  | `en`, `zh`, `ja`, `ko` | Announcement language. Defaults to `en`.                |
| `plainText` | `bool`   | `false`  | `true` or `false`      | Whether to return plain text body. Defaults to `false`. |

#### Response

| Name        | Type                 | Required | Description                                                                                                                    |
| ----------- | -------------------- | -------- | ------------------------------------------------------------------------------------------------------------------------------ |
| `code`      | `int32`              | `true`   | Response status code. `0` means success.                                                                                       |
| `data`      | `AnnouncementDetail` | `true`   | Response data when `code` is `0`. See [`AnnouncementDetail`](/documentation/trading-api/rest-v1/schema.md#announcementdetail). |
| `timestamp` | `uint64`             | `true`   | Response timestamp in milliseconds.                                                                                            |
