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

# Create an account

> Create an API account and get the API key ID and secret key that sign your requests.

`POST /accounts` creates an API account and returns its API key ID (`apiKey`) and secret key (`secretKey`). The `password` you send becomes the account's passphrase. You sign every request to the signed endpoints with these three values, as described in [Sign requests](/authentication/sign-requests). This endpoint is public: it takes no signed headers.

Public endpoint. Send `Content-Type: application/json` and a JSON body with `name` (at least 6 characters) and `password` (at least 8 characters), with no signed headers. Each successful call creates a real, active account, so never call it from tests or retry loops. The response is the only time `secretKey` is returned: store it, the API key ID and the password (now the passphrase) before you do anything else.

## What you get back

| Credential | Where it comes from | How you use it | Environment variable |
| - | - | - | - |
| API key ID | `data.apiKey`, a UUID | Sent as `x-api-key-id` on every signed request. | `OLYMPEX_API_KEY_ID` |
| Secret key | `data.secretKey`, a random string shown once | The HMAC key for `x-signature`, used as the string itself: don't decode it or check its format. You never send it. | `OLYMPEX_SECRET_KEY` |
| Passphrase | The `password` you sent | Sent as `x-passphrase` on every signed request. | `OLYMPEX_PASSPHRASE` |

The account can call signed endpoints as soon as this call returns. There is no activation step. [Credentials](/authentication/credentials) covers how the three values work together.

## Choose the passphrase

* **Use at least 24 random characters from a password manager.** The API accepts 8 or more, but the passphrase guards your account as much as the secret key does.
* **Use printable ASCII only, with no leading or trailing spaces.** The API trims the `x-passphrase` header, and browsers can't send characters outside ISO-8859-1. A passphrase with leading or trailing spaces, or with characters a client can't send in a header, creates an account you can't sign requests for. Printable ASCII avoids both.
* **Protect it like the secret key.** Every signed request carries it in `x-passphrase`, so anything that logs request headers can capture it.
* **`name` is only a label.** It needs at least 6 characters, and names don't have to be unique.

A `name` shorter than 6 characters or a `password` shorter than 8 fails with `400 VALIDATION_ERROR`, and the detail reads `"name is required"` or `"password is required"` even when the field is present.

## Store the credentials right away

The secret key appears in this response and nowhere else. Olympex stores only a hash of the passphrase and an encrypted copy of the secret key, and no endpoint returns either one again. Write all three values to your secrets manager before you do anything else, and keep the response out of logs, tickets and chat.

The `200` example on this page shows the API key ID and the secret key of the [known-answer vectors](/authentication/sign-requests#known-answer-vector), which are test values, not a real account. Your secret key is a different random string.

<Warning>
  Credentials can't be rotated or changed after you create them. If any of the three values leaks, email [partners@olympex.io](mailto:partners@olympex.io) to deactivate the account, then create a new one and switch your integration to it.
</Warning>

## Test and production accounts

The console on this page creates a real account with a passphrase generated in your browser. Use it to try the API. If your browser blocks the call, use the cURL example on this page or [create a key from a terminal](/get-started/create-an-api-key#create-a-key-from-a-terminal). For production, create a separate account from a machine you control, with a passphrase from your password manager, and load the credentials from your secrets manager at runtime. There is no sandbox: both accounts call the same production API. Limit orders and DCA strategies created with either account are real orders, and only the account that created them can read, change or cancel them. [Going to production](/guides/going-to-production) has the full checklist.

<Note>
  Every successful call creates a new account. If a request fails before you read the response, an account may already exist whose secret key you never saw. Create another one by hand (names don't have to be unique), and never retry this call automatically.
</Note>

<RequestExample>
  ```bash cURL theme={null}
  # Create an API account. The response contains your secret key, once: it goes to olympex.env, never to the terminal.
  # Needs curl, jq and OLYMPEX_PASSPHRASE in the environment. To set it without echo:
  #   read -rs OLYMPEX_PASSPHRASE && export OLYMPEX_PASSPHRASE
  # jq builds the body from the environment, so the passphrase is JSON-escaped and never appears in a command line.
  ( umask 077
    OLYMPEX_ACCOUNT_NAME="acme-trading-desk" jq -n '{name: env.OLYMPEX_ACCOUNT_NAME, password: env.OLYMPEX_PASSPHRASE}' |
      curl -sS -X POST "https://api-rest.olympex.io/api/v1/accounts" \
        -H "content-type: application/json" \
        --data-binary @- -o olympex-account.json )
  # Move the two values to a new olympex.env, readable only by you, and print the API key ID.
  if jq -e .success olympex-account.json >/dev/null; then
    ( umask 077; set -C
      jq -r '"OLYMPEX_API_KEY_ID=\(.data.apiKey)\nOLYMPEX_SECRET_KEY=\(.data.secretKey)"' olympex-account.json > olympex.env ) &&
      rm olympex-account.json && grep '^OLYMPEX_API_KEY_ID=' olympex.env
  else
    cat olympex-account.json; echo
  fi
  ```

  ```ts TypeScript theme={null}
  // Node.js 18+. Run once, from a machine you control: every successful call creates a real account.
  import { existsSync, writeFileSync } from "node:fs";

  const ENV_FILE = "olympex.env";
  const passphrase = process.env.OLYMPEX_PASSPHRASE ?? ""; // from your password manager
  if (passphrase.length < 24 || !/^[\x20-\x7E]+$/.test(passphrase) || passphrase.trim() !== passphrase) {
    throw new Error("Set OLYMPEX_PASSPHRASE to 24+ printable ASCII characters with no leading or trailing spaces.");
  }
  // Check before creating the account: the secret key can't be fetched again if the write fails.
  if (existsSync(ENV_FILE)) throw new Error(`${ENV_FILE} already exists. Move it before you create another account.`);

  const response = await fetch("https://api-rest.olympex.io/api/v1/accounts", {
    method: "POST",
    headers: { "content-type": "application/json" },
    body: JSON.stringify({ name: "acme-trading-desk", password: passphrase }),
    signal: AbortSignal.timeout(35_000),
  });
  const payload = await response.json();
  if (!response.ok || payload.success !== true) {
    throw new Error(`${response.status} ${payload.error?.code ?? ""} ${payload.error?.message ?? payload.message ?? ""}`.trim());
  }

  // The secret key is shown once. Keep it out of logs: write it to a file only you can read,
  // then move it into your secrets manager.
  const { apiKey, secretKey } = payload.data as { apiKey: string; secretKey: string };
  writeFileSync(ENV_FILE, `OLYMPEX_API_KEY_ID=${apiKey}\nOLYMPEX_SECRET_KEY=${secretKey}\n`, { mode: 0o600, flag: "wx" });
  console.log(`Created API key ID ${apiKey}. Secret key written to ${ENV_FILE}.`);
  ```

  ```python Python theme={null}
  # Python 3.8+. Run once, from a machine you control: every successful call creates a real account.
  import os
  import re

  import requests

  ENV_FILE = "olympex.env"
  passphrase = os.environ.get("OLYMPEX_PASSPHRASE", "")  # from your password manager
  if len(passphrase) < 24 or not re.fullmatch(r"[\x20-\x7e]+", passphrase) or passphrase.strip() != passphrase:
      raise SystemExit("Set OLYMPEX_PASSPHRASE to 24+ printable ASCII characters with no leading or trailing spaces.")
  # Check before creating the account: the secret key can't be fetched again if the write fails.
  if os.path.exists(ENV_FILE):
      raise SystemExit(f"{ENV_FILE} already exists. Move it before you create another account.")

  response = requests.post(
      "https://api-rest.olympex.io/api/v1/accounts",
      json={"name": "acme-trading-desk", "password": passphrase},
      timeout=35,
  )
  payload = response.json()
  if not response.ok or payload.get("success") is not True:
      error = payload.get("error") or {}
      raise SystemExit(f"{response.status_code} {error.get('code', '')} {error.get('message') or payload.get('message', '')}")

  # The secret key is shown once. Keep it out of logs: write it to a file only you can read,
  # then move it into your secrets manager.
  data = payload["data"]
  fd = os.open(ENV_FILE, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
  with os.fdopen(fd, "w") as env_file:
      env_file.write(f"OLYMPEX_API_KEY_ID={data['apiKey']}\nOLYMPEX_SECRET_KEY={data['secretKey']}\n")
  print(f"Created API key ID {data['apiKey']}. Secret key written to {ENV_FILE}.")
  ```
</RequestExample>

<ResponseExample>
  ```json 200 theme={null}
  {
    "success": true,
    "data": {
      "message": "ACCOUNT_CREATED",
      "apiKey": "00000000-0000-4000-8000-000000000000",
      "secretKey": "aol_DocsTestVector_notARealSecret_000000000"
    },
    "meta": {
      "requestId": "ENMcEjviIAMEMYg=",
      "version": "v1"
    }
  }
  ```

  ```json 400 theme={null}
  {
    "success": false,
    "error": {
      "code": "VALIDATION_ERROR",
      "message": "Invalid request body",
      "details": [
        {
          "field": "name",
          "message": "name is required"
        },
        {
          "field": "password",
          "message": "password is required"
        }
      ]
    },
    "meta": {
      "requestId": "ENUm9iggoAMEMhw=",
      "version": "v1"
    }
  }
  ```
</ResponseExample>


## OpenAPI

````yaml api-reference/openapi.json POST /accounts
openapi: 3.0.3
info:
  title: Olympex REST API
  version: 1.0.0
  description: >-
    Aggregated swap quotes and transactions, limit orders and DCA strategies on
    EVM chains, plus the chain and token catalog. Every response uses the same
    envelope, except the API gateway's own responses (`{"message": …}`): `401`
    or `403` when the signed headers are missing or rejected, `429` when it
    throttles requests, and `500` or `503` when it can't get a response from
    Olympex in time. Chain IDs are integers on every endpoint. Signed endpoints
    require four signed headers; see [Sign
    requests](https://docs.olympex.io/authentication/sign-requests).
  termsOfService: https://docs.olympex.io/legal/terms
  contact:
    name: Olympex partnerships
    email: partners@olympex.io
    url: https://docs.olympex.io
servers:
  - url: https://api-rest.olympex.io/api/v1
    description: Olympex REST API v1
security: []
tags:
  - name: Accounts
    description: Create API credentials.
  - name: Quotes
    description: Aggregated single-chain and cross-chain quotes.
  - name: Swap
    description: Unsigned transaction calldata for a quoted route.
  - name: TxStatus
    description: Track a cross-chain transfer after you broadcast it.
  - name: Transactions
    description: On-chain status of a transaction you broadcast.
  - name: Chains and tokens
    description: Enabled chains and the tokens listed on each.
  - name: Limit orders
    description: Orders that Olympex executes when the market reaches your price.
  - name: DCA
    description: Strategies that buy a token in equal orders over time.
  - name: Docs
    description: Machine-readable API description.
paths:
  /accounts:
    post:
      tags:
        - Accounts
      summary: Create an account
      description: >-
        Creates an API account and returns its API key ID and secret key. The
        `password` you send becomes the account passphrase. The secret key is
        returned once and can't be retrieved again; store it before you do
        anything else.
      operationId: createAccount
      requestBody:
        required: true
        content:
          application/json:
            schema:
              $ref: '#/components/schemas/CreateAccountRequest'
            example:
              name: acme-trading-desk
              password: <at least 24 random printable-ASCII characters>
      responses:
        '200':
          description: Account created. The account can call signed endpoints immediately.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/CreateAccountSuccessResponse'
              example:
                success: true
                data:
                  message: ACCOUNT_CREATED
                  apiKey: 00000000-0000-4000-8000-000000000000
                  secretKey: aol_DocsTestVector_notARealSecret_000000000
                meta:
                  requestId: ENMcEjviIAMEMYg=
                  version: v1
        '400':
          description: >-
            Validation error. A `name` shorter than 6 characters or a `password`
            shorter than 8 characters reports `"… is required"`.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error:
                  code: VALIDATION_ERROR
                  message: Invalid request body
                  details:
                    - field: name
                      message: name is required
                    - field: password
                      message: password is required
                meta:
                  requestId: ENUm9iggoAMEMhw=
                  version: v1
        '500':
          description: Internal error.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/ErrorResponse'
              example:
                success: false
                error:
                  code: INTERNAL_ERROR
                  message: Unexpected internal error
                  details: []
                meta:
                  requestId: ENKCbj5BoAMEblA=
                  version: v1
      security: []
components:
  schemas:
    CreateAccountRequest:
      type: object
      required:
        - name
        - password
      properties:
        name:
          type: string
          minLength: 6
          description: >-
            Label for the account, at least 6 characters. Names don't have to be
            unique.
          example: acme-trading-desk
        password:
          type: string
          minLength: 8
          description: >-
            Becomes the account passphrase, sent as `x-passphrase` on every
            signed request. Use at least 24 random printable-ASCII characters
            with no leading or trailing spaces: the API trims the header, and
            browsers can't send characters outside ISO-8859-1, so other values
            create an account you can't use.
    CreateAccountSuccessResponse:
      type: object
      required:
        - success
        - data
        - meta
      properties:
        success:
          type: boolean
          enum:
            - true
        data:
          type: object
          required:
            - message
            - apiKey
            - secretKey
          properties:
            message:
              type: string
              default: ACCOUNT_CREATED
            apiKey:
              type: string
              format: uuid
              description: Your API key ID. Send it as `x-api-key-id`.
            secretKey:
              type: string
              description: >-
                Your secret key, used as the HMAC key when you sign. Returned
                only in this response.
        meta:
          $ref: '#/components/schemas/Meta'
    ErrorResponse:
      type: object
      required:
        - success
        - error
        - meta
      properties:
        success:
          type: boolean
          enum:
            - false
        error:
          $ref: '#/components/schemas/ErrorBody'
        meta:
          $ref: '#/components/schemas/Meta'
    Meta:
      type: object
      required:
        - requestId
        - version
      properties:
        requestId:
          type: string
          description: Unique ID for this request. Include it when you contact support.
        version:
          type: string
          default: v1
          description: API version that served the request.
        accountType:
          type: string
          description: >-
            Account type resolved by the signature check, for example
            `integrator`. Present when the request was authenticated; absent on
            the `404` for an unknown path or method.
        apiKeyId:
          type: string
          format: uuid
          description: >-
            The API key ID that signed the request. Present when the request was
            authenticated; absent on the `404` for an unknown path or method.
    ErrorBody:
      type: object
      required:
        - code
        - message
        - details
      properties:
        code:
          type: string
          description: >-
            Machine-readable error code. `VALIDATION_ERROR` (400),
            `UNAUTHORIZED` (401), `FORBIDDEN` (403), `NOT_FOUND` (404, also for
            an unknown path or method), `CONFLICT` (409), `NO_ROUTE` (422), and
            for 500: `QUOTE_ERROR`, `CROSS_CHAIN_QUOTE_ERROR`, `SWAP_ERROR`,
            `CROSS_CHAIN_SWAP_ERROR`, `SUPPORT_CHAIN_ERROR`,
            `ENABLED_CHAINS_ERROR`, `TOKEN_LIST_ERROR`, `TX_STATUS_ERROR`,
            `INTERNAL_ERROR`. Olympex can add codes: handle an unknown code by
            its HTTP status.
        message:
          type: string
          description: Human-readable summary. Don't parse it.
        details:
          type: array
          items:
            $ref: '#/components/schemas/ErrorDetail'
    ErrorDetail:
      type: object
      additionalProperties: true
      description: >-
        Validation errors carry `field` and `message`; other errors carry
        `message` only.
      properties:
        field:
          type: string
          description: >-
            Dot path of the invalid field, for example `params.chainId`. Empty
            for the top level.
        message:
          type: string

````

This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.