# API Reference

Explore all nine public integration endpoints, grouped by resource.

Source: https://docs.grapl.io/developers/api/reference

Use your deployment origin and an `x-api-key` integration credential. This reference renders the complete reviewed public OpenAPI 3.1.0 contract. [Authentication guide](https://docs.grapl.io/developers/api/authentication) · [Download OpenAPI](https://docs.grapl.io/openapi.json).

Build, order and submit currently return 503 TRADING_NOT_CONFIGURED. The
interactive playground is unavailable until a service origin is configured.

Request samples require your origin, key and valid input values. Captured response fixtures are labeled; responses without a verified example show Empty while retaining their schemas.

## Pools

### Discover pools by mint
`GET /v1/pools`

Lists pools discovered by the configured grapl indexer. Supply quoteMint to find a direct pair. Empty results return 200. Catalog inclusion is not a quote or an execution guarantee; use /v1/swap/quote to compare supported pools for an amount.

**Authentication:** `x-api-key` integration credential.

## Parameters

| Name | Location | Required | Type and constraints | Description |
| --- | --- | --- | --- | --- |
| mint | query | Yes | string; solana-address | Find indexed grapl pools containing this SPL mint on either side. |
| quoteMint | query | No | string; solana-address | Optional second mint. Restricts discovery to the direct pair, in either order. |
| limit | query | No | integer; minimum: 1; maximum: 100; default: 20 | Maximum pools per page, ordered by pool address. |
| cursor | query | No | string; solana-address | Exclusive pool address cursor returned as nextCursor. Pagination is a live catalog view. |

## Request example

```sh
curl --fail-with-body --get "${GRAPL_API_ORIGIN}/v1/pools" \
  --header "x-api-key: ${GRAPL_API_KEY}" \
  --data-urlencode "mint=${MINT}"
```

## Responses

| HTTP status | Meaning |
| --- | --- |
| 200 | Successful request |
| 400 | Invalid parameters, unknown fields or malformed request body. |
| 401 | Missing, expired or revoked credential. |
| 403 | Credential audience or team does not permit this operation. |
| 404 | Requested resource or indexed mint pair was not found. |
| 409 | Resource is no longer in the required state. |
| 413 | Request body exceeds the documented size limit. |
| 422 | No supported complete quote, too many candidates or amount limits exceeded. |
| 429 | Team request quota or stream capacity exhausted. Respect Retry-After when supplied. |
| 500 | Unexpected server failure. |
| 503 | Required service, quote worker or execution bridge is unavailable. |

### 200 fields

| Field | Type and constraints | Required | Description |
| --- | --- | --- | --- |
| response.success | boolean; one of true | Yes |  |
| response.data | object | Yes |  |
| response.data.pools | array | Yes |  |
| response.data.pools[].address | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.pools[].mintA | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.pools[].mintB | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.pools[].liquidity | string; maxLength: 39 | Yes | Native liquidity units, not TVL or a ranking score. |
| response.data.pools[].observedSlot | string; maxLength: 39 | Yes | Slot of the indexer snapshot. Quotes independently refresh on-chain accounts. |
| response.data.pools[].hookProgram | string; solana-address | Yes | System program address means no hook. Discovery does not imply quote support. |
| response.data.pools[].tradeFeePpm | integer; minimum: 0 | Yes | Configured base fee in parts per million; hooks may change effective fees. |
| response.data.pools[].mode | integer | Yes | 0: concentrated liquidity; 1: virtual-reserve bonding curve. |
| response.data.nextCursor | string / null; solana-address | Yes | Base58-encoded Solana public key. |

### 400 fields

| Field | Type and constraints | Required | Description |
| --- | --- | --- | --- |
| response.success | boolean; one of false | Yes |  |
| response.error | object | Yes |  |
| response.error.code | string | Yes | Stable machine-readable error code. |
| response.error.message | string | Yes | Human-readable failure description; never contains credentials. |

## Swaps

### Quote the best direct pool
`GET /v1/swap/quote`

Provide the input/output mint pair and amount; no pool address is required or accepted. Compares up to 50 indexed candidates using the native engine, including fees and liquidity. ExactIn maximizes output; ExactOut minimizes input. Ties use lexical pool address order. 404 NO_POOLS means the pair is absent; 422 NO_QUOTABLE_POOL means no supported pool can fill the whole amount. RPC failures abort selection with 503, never a partial ranking. Exceeding 50 candidates returns 422 TOO_MANY_POOLS.

**Authentication:** `x-api-key` integration credential.

## Parameters

| Name | Location | Required | Type and constraints | Description |
| --- | --- | --- | --- | --- |
| inputMint | query | Yes | string; solana-address | SPL mint to sell. Paired with outputMint for automatic pool selection. |
| outputMint | query | Yes | string; solana-address | SPL mint to receive; must differ from inputMint. |
| amount | query | Yes | string; maxLength: 20 | Positive u64 token atoms as a decimal string. Input atoms for ExactIn; output atoms for ExactOut. For 6 decimals, 1000000 represents one token. |
| swapMode | query | No | string; one of ExactIn, ExactOut; default: ExactIn | ExactIn fixes input and maximizes output; ExactOut fixes output and minimizes input. |
| slippageBps | query | No | integer; minimum: 0; maximum: 10000; default: 50 | Slippage tolerance in basis points: 50 = 0.5%. Sets the returned threshold; does not change which pool has the best nominal quote. |
| taker | query | No | string; solana-address | Optional wallet context for future contextual adapters. Providing an address is not proof of authorization. |

## Request example

```sh
curl --fail-with-body --get "${GRAPL_API_ORIGIN}/v1/swap/quote" \
  --header "x-api-key: ${GRAPL_API_KEY}" \
  --data-urlencode "inputMint=${INPUT_MINT}" \
  --data-urlencode "outputMint=${OUTPUT_MINT}" \
  --data-urlencode "amount=${AMOUNT}"
```

## Responses

| HTTP status | Meaning |
| --- | --- |
| 200 | Successful request |
| 400 | Invalid parameters, unknown fields or malformed request body. |
| 401 | Missing, expired or revoked credential. |
| 403 | Credential audience or team does not permit this operation. |
| 404 | Requested resource or indexed mint pair was not found. |
| 409 | Resource is no longer in the required state. |
| 413 | Request body exceeds the documented size limit. |
| 422 | No supported complete quote, too many candidates or amount limits exceeded. |
| 429 | Team request quota or stream capacity exhausted. Respect Retry-After when supplied. |
| 500 | Unexpected server failure. |
| 503 | Required service, quote worker or execution bridge is unavailable. |

### 200 fields

| Field | Type and constraints | Required | Description |
| --- | --- | --- | --- |
| response.success | boolean; one of true | Yes |  |
| response.data | object | Yes |  |
| response.data.pool | string; solana-address | Yes | Selected direct pool. Output only; callers specify mints, not a pool. |
| response.data.requestId | string | Yes |  |
| response.data.inputMint | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.outputMint | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.inAmount | string; maxLength: 39 | Yes | Gross input in token atoms, including the trade fee. |
| response.data.outAmount | string; maxLength: 39 | Yes | Expected output in token atoms after the pool trade fee. |
| response.data.otherAmountThreshold | string; maxLength: 39 | Yes | Minimum output for ExactIn; maximum input for ExactOut. |
| response.data.swapMode | string; one of ExactIn, ExactOut | Yes |  |
| response.data.slippageBps | integer | Yes |  |
| response.data.priceImpactPct | string / null | Yes | Null when a price-impact metric is unavailable; never fabricated as zero. |
| response.data.contextSlot | string; maxLength: 39 | Yes | Confirmed RPC snapshot slot for the selected pool. Other candidates may use different slots. |
| response.data.expiresAt | string; date-time | Yes | UTC quote expiry, 10 seconds after selection starts. No liquidity is reserved. |
| response.data.fees | array | Yes |  |
| response.data.fees[].mint | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.fees[].amount | string; maxLength: 39 | Yes |  |
| response.data.fees[].kind | string; one of trade, lp, protocol, hook | Yes | trade is the total input trade fee; it is not an additional fee or an LP/protocol allocation breakdown. |

### 400 fields

| Field | Type and constraints | Required | Description |
| --- | --- | --- | --- |
| response.success | boolean; one of false | Yes |  |
| response.error | object | Yes |  |
| response.error.code | string | Yes | Stable machine-readable error code. |
| response.error.message | string | Yes | Human-readable failure description; never contains credentials. |

## Isolated response fixture

Captured from the API's isolated selection-test fakes. Test addresses and amounts illustrate the contract, not a market quote. Only the random request ID is normalized.

```json
{
  "success": true,
  "data": {
    "pool": "11111111111111111111111111111111",
    "requestId": "isolated-selection-fixture",
    "inputMint": "11111111111111111111111111111111",
    "outputMint": "So11111111111111111111111111111111111111112",
    "inAmount": "1000",
    "outAmount": "990",
    "otherAmountThreshold": "985",
    "swapMode": "ExactIn",
    "slippageBps": 50,
    "priceImpactPct": null,
    "contextSlot": "42",
    "expiresAt": "2026-10-12T12:30:10.000Z",
    "fees": [
      {
        "mint": "11111111111111111111111111111111",
        "amount": "3",
        "kind": "trade"
      }
    ]
  }
}
```

### Build unsigned swap instructions
`GET /v1/swap/build`

Returns account privileges and instruction bytes for wallet signing when the execution bridge is configured. Currently returns 503 TRADING_NOT_CONFIGURED.

**Authentication:** `x-api-key` integration credential.

## Parameters

| Name | Location | Required | Type and constraints | Description |
| --- | --- | --- | --- | --- |
| inputMint | query | Yes | string; solana-address | SPL mint to sell. Paired with outputMint for automatic pool selection. |
| outputMint | query | Yes | string; solana-address | SPL mint to receive; must differ from inputMint. |
| amount | query | Yes | string; maxLength: 20 | Positive u64 token atoms as a decimal string. Input atoms for ExactIn; output atoms for ExactOut. For 6 decimals, 1000000 represents one token. |
| swapMode | query | No | string; one of ExactIn, ExactOut; default: ExactIn | ExactIn fixes input and maximizes output; ExactOut fixes output and minimizes input. |
| slippageBps | query | No | integer; minimum: 0; maximum: 10000; default: 50 | Slippage tolerance in basis points: 50 = 0.5%. Sets the returned threshold; does not change which pool has the best nominal quote. |
| taker | query | Yes | string; solana-address | Wallet that will sign the swap; required for instruction construction. |

## Request example

```sh
curl --fail-with-body --get "${GRAPL_API_ORIGIN}/v1/swap/build" \
  --header "x-api-key: ${GRAPL_API_KEY}" \
  --data-urlencode "inputMint=${INPUT_MINT}" \
  --data-urlencode "outputMint=${OUTPUT_MINT}" \
  --data-urlencode "amount=${AMOUNT}" \
  --data-urlencode "taker=${TAKER}"
```

## Responses

| HTTP status | Meaning |
| --- | --- |
| 200 | Successful request |
| 400 | Invalid parameters, unknown fields or malformed request body. |
| 401 | Missing, expired or revoked credential. |
| 403 | Credential audience or team does not permit this operation. |
| 404 | Requested resource or indexed mint pair was not found. |
| 409 | Resource is no longer in the required state. |
| 413 | Request body exceeds the documented size limit. |
| 422 | No supported complete quote, too many candidates or amount limits exceeded. |
| 429 | Team request quota or stream capacity exhausted. Respect Retry-After when supplied. |
| 500 | Unexpected server failure. |
| 503 | Required service, quote worker or execution bridge is unavailable. |

### 200 fields

| Field | Type and constraints | Required | Description |
| --- | --- | --- | --- |
| response.success | boolean; one of true | Yes |  |
| response.data | object | Yes |  |
| response.data.pool | string; solana-address | Yes | Selected direct pool. Output only; callers specify mints, not a pool. |
| response.data.requestId | string | Yes |  |
| response.data.inputMint | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.outputMint | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.inAmount | string; maxLength: 39 | Yes | Gross input in token atoms, including the trade fee. |
| response.data.outAmount | string; maxLength: 39 | Yes | Expected output in token atoms after the pool trade fee. |
| response.data.otherAmountThreshold | string; maxLength: 39 | Yes | Minimum output for ExactIn; maximum input for ExactOut. |
| response.data.swapMode | string; one of ExactIn, ExactOut | Yes |  |
| response.data.slippageBps | integer | Yes |  |
| response.data.priceImpactPct | string / null | Yes | Null when a price-impact metric is unavailable; never fabricated as zero. |
| response.data.contextSlot | string; maxLength: 39 | Yes | Confirmed RPC snapshot slot for the selected pool. Other candidates may use different slots. |
| response.data.expiresAt | string; date-time | Yes | UTC quote expiry, 10 seconds after selection starts. No liquidity is reserved. |
| response.data.fees | array | Yes |  |
| response.data.fees[].mint | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.fees[].amount | string; maxLength: 39 | Yes |  |
| response.data.fees[].kind | string; one of trade, lp, protocol, hook | Yes | trade is the total input trade fee; it is not an additional fee or an LP/protocol allocation breakdown. |
| response.data.computeBudgetInstructions | array | Yes |  |
| response.data.computeBudgetInstructions[].programId | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.computeBudgetInstructions[].accounts | array | Yes |  |
| response.data.computeBudgetInstructions[].accounts[].pubkey | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.computeBudgetInstructions[].accounts[].isSigner | boolean | Yes |  |
| response.data.computeBudgetInstructions[].accounts[].isWritable | boolean | Yes |  |
| response.data.computeBudgetInstructions[].data | string | Yes | Base64 instruction bytes |
| response.data.setupInstructions | array | Yes |  |
| response.data.setupInstructions[].programId | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.setupInstructions[].accounts | array | Yes |  |
| response.data.setupInstructions[].accounts[].pubkey | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.setupInstructions[].accounts[].isSigner | boolean | Yes |  |
| response.data.setupInstructions[].accounts[].isWritable | boolean | Yes |  |
| response.data.setupInstructions[].data | string | Yes | Base64 instruction bytes |
| response.data.swapInstruction | object | Yes |  |
| response.data.swapInstruction.programId | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.swapInstruction.accounts | array | Yes |  |
| response.data.swapInstruction.accounts[].pubkey | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.swapInstruction.accounts[].isSigner | boolean | Yes |  |
| response.data.swapInstruction.accounts[].isWritable | boolean | Yes |  |
| response.data.swapInstruction.data | string | Yes | Base64 instruction bytes |
| response.data.cleanupInstruction | object / null | Yes |  |
| response.data.cleanupInstruction.programId | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.cleanupInstruction.accounts | array | Yes |  |
| response.data.cleanupInstruction.accounts[].pubkey | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.cleanupInstruction.accounts[].isSigner | boolean | Yes |  |
| response.data.cleanupInstruction.accounts[].isWritable | boolean | Yes |  |
| response.data.cleanupInstruction.data | string | Yes | Base64 instruction bytes |
| response.data.otherInstructions | array | Yes |  |
| response.data.otherInstructions[].programId | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.otherInstructions[].accounts | array | Yes |  |
| response.data.otherInstructions[].accounts[].pubkey | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.otherInstructions[].accounts[].isSigner | boolean | Yes |  |
| response.data.otherInstructions[].accounts[].isWritable | boolean | Yes |  |
| response.data.otherInstructions[].data | string | Yes | Base64 instruction bytes |
| response.data.addressesByLookupTableAddress | object | Yes |  |
| response.data.blockhash | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.lastValidBlockHeight | string; maxLength: 39 | Yes |  |

### 400 fields

| Field | Type and constraints | Required | Description |
| --- | --- | --- | --- |
| response.success | boolean; one of false | Yes |  |
| response.error | object | Yes |  |
| response.error.code | string | Yes | Stable machine-readable error code. |
| response.error.message | string | Yes | Human-readable failure description; never contains credentials. |

### Build an unsigned swap transaction
`GET /v1/swap/order`

Execution-bridge contract. Currently returns 503 TRADING_NOT_CONFIGURED; a quote alone does not produce a transaction.

**Authentication:** `x-api-key` integration credential.

## Parameters

| Name | Location | Required | Type and constraints | Description |
| --- | --- | --- | --- | --- |
| inputMint | query | Yes | string; solana-address | SPL mint to sell. Paired with outputMint for automatic pool selection. |
| outputMint | query | Yes | string; solana-address | SPL mint to receive; must differ from inputMint. |
| amount | query | Yes | string; maxLength: 20 | Positive u64 token atoms as a decimal string. Input atoms for ExactIn; output atoms for ExactOut. For 6 decimals, 1000000 represents one token. |
| swapMode | query | No | string; one of ExactIn, ExactOut; default: ExactIn | ExactIn fixes input and maximizes output; ExactOut fixes output and minimizes input. |
| slippageBps | query | No | integer; minimum: 0; maximum: 10000; default: 50 | Slippage tolerance in basis points: 50 = 0.5%. Sets the returned threshold; does not change which pool has the best nominal quote. |
| taker | query | No | string; solana-address | Optional wallet context for future contextual adapters. Providing an address is not proof of authorization. |

## Request example

```sh
curl --fail-with-body --get "${GRAPL_API_ORIGIN}/v1/swap/order" \
  --header "x-api-key: ${GRAPL_API_KEY}" \
  --data-urlencode "inputMint=${INPUT_MINT}" \
  --data-urlencode "outputMint=${OUTPUT_MINT}" \
  --data-urlencode "amount=${AMOUNT}"
```

## Responses

| HTTP status | Meaning |
| --- | --- |
| 200 | Successful request |
| 400 | Invalid parameters, unknown fields or malformed request body. |
| 401 | Missing, expired or revoked credential. |
| 403 | Credential audience or team does not permit this operation. |
| 404 | Requested resource or indexed mint pair was not found. |
| 409 | Resource is no longer in the required state. |
| 413 | Request body exceeds the documented size limit. |
| 422 | No supported complete quote, too many candidates or amount limits exceeded. |
| 429 | Team request quota or stream capacity exhausted. Respect Retry-After when supplied. |
| 500 | Unexpected server failure. |
| 503 | Required service, quote worker or execution bridge is unavailable. |

### 200 fields

| Field | Type and constraints | Required | Description |
| --- | --- | --- | --- |
| response.success | boolean; one of true | Yes |  |
| response.data | object | Yes |  |
| response.data.pool | string; solana-address | Yes | Selected direct pool. Output only; callers specify mints, not a pool. |
| response.data.requestId | string | Yes |  |
| response.data.inputMint | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.outputMint | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.inAmount | string; maxLength: 39 | Yes | Gross input in token atoms, including the trade fee. |
| response.data.outAmount | string; maxLength: 39 | Yes | Expected output in token atoms after the pool trade fee. |
| response.data.otherAmountThreshold | string; maxLength: 39 | Yes | Minimum output for ExactIn; maximum input for ExactOut. |
| response.data.swapMode | string; one of ExactIn, ExactOut | Yes |  |
| response.data.slippageBps | integer | Yes |  |
| response.data.priceImpactPct | string / null | Yes | Null when a price-impact metric is unavailable; never fabricated as zero. |
| response.data.contextSlot | string; maxLength: 39 | Yes | Confirmed RPC snapshot slot for the selected pool. Other candidates may use different slots. |
| response.data.expiresAt | string; date-time | Yes | UTC quote expiry, 10 seconds after selection starts. No liquidity is reserved. |
| response.data.fees | array | Yes |  |
| response.data.fees[].mint | string; solana-address | Yes | Base58-encoded Solana public key. |
| response.data.fees[].amount | string; maxLength: 39 | Yes |  |
| response.data.fees[].kind | string; one of trade, lp, protocol, hook | Yes | trade is the total input trade fee; it is not an additional fee or an LP/protocol allocation breakdown. |
| response.data.transaction | string / null; minLength: 4; maxLength: 1644 | Yes |  |
| response.data.lastValidBlockHeight | string / null; maxLength: 39 | Yes |  |

### 400 fields

| Field | Type and constraints | Required | Description |
| --- | --- | --- | --- |
| response.success | boolean; one of false | Yes |  |
| response.error | object | Yes |  |
| response.error.code | string | Yes | Stable machine-readable error code. |
| response.error.message | string | Yes | Human-readable failure description; never contains credentials. |

### Submit a signed transaction
`POST /v1/swap/submit`

Submit a signed transaction. Submission does not establish confirmation or settlement.

**Authentication:** `x-api-key` integration credential.

## Parameters

No query or path parameters.

## Request body

Content-Type: `application/json`.

| Field | Type and constraints | Required | Description |
| --- | --- | --- | --- |
| body.signedTransaction | string; minLength: 4; maxLength: 1644 | Yes |  |

## Request example

```sh
curl --fail-with-body --request POST "${GRAPL_API_ORIGIN}/v1/swap/submit" \
  --header "x-api-key: ${GRAPL_API_KEY}" \
  --header "Content-Type: application/json" \
  --data @signed-request.json
```

`signed-request.json` must contain the documented body with your application's signed transaction. This request currently returns 503; it is not a working submission flow.

## Responses

| HTTP status | Meaning |
| --- | --- |
| 202 | Successful request |
| 400 | Invalid parameters, unknown fields or malformed request body. |
| 401 | Missing, expired or revoked credential. |
| 403 | Credential audience or team does not permit this operation. |
| 404 | Requested resource or indexed mint pair was not found. |
| 409 | Resource is no longer in the required state. |
| 413 | Request body exceeds the documented size limit. |
| 422 | No supported complete quote, too many candidates or amount limits exceeded. |
| 429 | Team request quota or stream capacity exhausted. Respect Retry-After when supplied. |
| 500 | Unexpected server failure. |
| 503 | Required service, quote worker or execution bridge is unavailable. |

### 202 fields

| Field | Type and constraints | Required | Description |
| --- | --- | --- | --- |
| response.success | boolean; one of true | Yes |  |
| response.data | object | Yes |  |
| response.data.signature | string | Yes |  |
| response.data.status | string; one of submitted | Yes |  |

### 400 fields

| Field | Type and constraints | Required | Description |
| --- | --- | --- | --- |
| response.success | boolean; one of false | Yes |  |
| response.error | object | Yes |  |
| response.error.code | string | Yes | Stable machine-readable error code. |
| response.error.message | string | Yes | Human-readable failure description; never contains credentials. |

## Transaction status

### Read an observed transaction outcome
`GET /v1/swap/status/{signature}`

Reads the configured observer. A not_observed result is not proof that a transaction is absent, pending or failed on chain.

**Authentication:** `x-api-key` integration credential.

## Parameters

| Name | Location | Required | Type and constraints | Description |
| --- | --- | --- | --- | --- |
| signature | path | Yes | string |  |

## Request example

```sh
curl --fail-with-body --get "${GRAPL_API_ORIGIN}/v1/swap/status/${SIGNATURE}" \
  --header "x-api-key: ${GRAPL_API_KEY}"
```

## Responses

| HTTP status | Meaning |
| --- | --- |
| 200 | Successful request |
| 400 | Invalid parameters, unknown fields or malformed request body. |
| 401 | Missing, expired or revoked credential. |
| 403 | Credential audience or team does not permit this operation. |
| 404 | Requested resource or indexed mint pair was not found. |
| 409 | Resource is no longer in the required state. |
| 413 | Request body exceeds the documented size limit. |
| 422 | No supported complete quote, too many candidates or amount limits exceeded. |
| 429 | Team request quota or stream capacity exhausted. Respect Retry-After when supplied. |
| 500 | Unexpected server failure. |
| 503 | Required service, quote worker or execution bridge is unavailable. |

### 200 fields

| Field | Type and constraints | Required | Description |
| --- | --- | --- | --- |
| response.success | boolean; one of true | Yes |  |
| response.data | object | Yes |  |
| response.data.signature | string | Yes |  |
| response.data.status | string; one of not_observed, finalized, failed | Yes |  |
| response.data.commitment | string; one of finalized | Yes |  |
| response.data.slot | string / null; maxLength: 39 | Yes |  |
| response.data.observedAt | string / null; date-time | Yes |  |

### 400 fields

| Field | Type and constraints | Required | Description |
| --- | --- | --- | --- |
| response.success | boolean; one of false | Yes |  |
| response.error | object | Yes |  |
| response.error.code | string | Yes | Stable machine-readable error code. |
| response.error.message | string | Yes | Human-readable failure description; never contains credentials. |

### Stream transaction status updates
`GET /v1/swap/status/{signature}/events`

Stream finalized observations from the configured chain/program. Resume using Last-Event-ID. Not-observed is not proof of absence on chain. Each connection counts as one API request; frames do not. Stop on complete.

**Authentication:** `x-api-key` integration credential.

## Parameters

| Name | Location | Required | Type and constraints | Description |
| --- | --- | --- | --- | --- |
| signature | path | Yes | string |  |
| last-event-id | header | No | string | Last delivered transaction.status event ID. Replays newer persisted events before following live updates. |

## Request example

```sh
curl --fail-with-body --get "${GRAPL_API_ORIGIN}/v1/swap/status/${SIGNATURE}/events" \
  --header "x-api-key: ${GRAPL_API_KEY}"
```

## Responses

| HTTP status | Meaning |
| --- | --- |
| 200 | SSE: snapshot, transaction.status (with id), heartbeat, complete, reconnect or stream-error. Only finalized/failed outcomes are observed currently. |
| 400 | Invalid parameters, unknown fields or malformed request body. |
| 401 | Missing, expired or revoked credential. |
| 403 | Credential audience or team does not permit this operation. |
| 404 | Requested resource or indexed mint pair was not found. |
| 409 | Resource is no longer in the required state. |
| 413 | Request body exceeds the documented size limit. |
| 422 | No supported complete quote, too many candidates or amount limits exceeded. |
| 429 | Team request quota or stream capacity exhausted. Respect Retry-After when supplied. |
| 500 | Unexpected server failure. |
| 503 | Required service, quote worker or execution bridge is unavailable. |

### 400 fields

| Field | Type and constraints | Required | Description |
| --- | --- | --- | --- |
| response.success | boolean; one of false | Yes |  |
| response.error | object | Yes |  |
| response.error.code | string | Yes | Stable machine-readable error code. |
| response.error.message | string | Yes | Human-readable failure description; never contains credentials. |

## Usage & limits

### Read remaining request quota
`GET /v1/rate-limits`

Read-only snapshot for the authenticated key’s team, shared by all its keys. Does not consume trading quota and remains available after exhaustion. Fixed UTC minute and calendar-month windows; concurrent requests can change these values immediately.

**Authentication:** `x-api-key` integration credential.

## Parameters

No query or path parameters.

## Request example

```sh
curl --fail-with-body --get "${GRAPL_API_ORIGIN}/v1/rate-limits" \
  --header "x-api-key: ${GRAPL_API_KEY}"
```

## Responses

| HTTP status | Meaning |
| --- | --- |
| 200 | Successful request |
| 400 | Invalid parameters, unknown fields or malformed request body. |
| 401 | Missing, expired or revoked credential. |
| 403 | Credential audience or team does not permit this operation. |
| 404 | Requested resource or indexed mint pair was not found. |
| 409 | Resource is no longer in the required state. |
| 413 | Request body exceeds the documented size limit. |
| 422 | No supported complete quote, too many candidates or amount limits exceeded. |
| 429 | Team request quota or stream capacity exhausted. Respect Retry-After when supplied. |
| 500 | Unexpected server failure. |
| 503 | Required service, quote worker or execution bridge is unavailable. |

### 200 fields

| Field | Type and constraints | Required | Description |
| --- | --- | --- | --- |
| response.success | boolean; one of true | Yes |  |
| response.data | object | Yes |  |
| response.data.scope | string; one of team | Yes |  |
| response.data.unit | string; one of requests | Yes |  |
| response.data.observedAt | string; date-time | Yes |  |
| response.data.canRequest | boolean | Yes |  |
| response.data.retryAfterSeconds | integer; minimum: 0 | Yes |  |
| response.data.minute | object | Yes |  |
| response.data.minute.limit | integer; minimum: 0 | Yes |  |
| response.data.minute.remaining | integer; minimum: 0 | Yes |  |
| response.data.minute.resetsAt | string; date-time | Yes |  |
| response.data.month | object | Yes |  |
| response.data.month.limit | integer; minimum: 0 | Yes |  |
| response.data.month.remaining | integer; minimum: 0 | Yes |  |
| response.data.month.resetsAt | string; date-time | Yes |  |

### 400 fields

| Field | Type and constraints | Required | Description |
| --- | --- | --- | --- |
| response.success | boolean; one of false | Yes |  |
| response.error | object | Yes |  |
| response.error.code | string | Yes | Stable machine-readable error code. |
| response.error.message | string | Yes | Human-readable failure description; never contains credentials. |

### Read current request consumption
`GET /v1/usage`

Read-only snapshot for the authenticated key’s team, shared by all its keys. Does not consume trading quota and remains available after exhaustion. Fixed UTC minute and calendar-month windows; concurrent requests can change these values immediately.

**Authentication:** `x-api-key` integration credential.

## Parameters

No query or path parameters.

## Request example

```sh
curl --fail-with-body --get "${GRAPL_API_ORIGIN}/v1/usage" \
  --header "x-api-key: ${GRAPL_API_KEY}"
```

## Responses

| HTTP status | Meaning |
| --- | --- |
| 200 | Successful request |
| 400 | Invalid parameters, unknown fields or malformed request body. |
| 401 | Missing, expired or revoked credential. |
| 403 | Credential audience or team does not permit this operation. |
| 404 | Requested resource or indexed mint pair was not found. |
| 409 | Resource is no longer in the required state. |
| 413 | Request body exceeds the documented size limit. |
| 422 | No supported complete quote, too many candidates or amount limits exceeded. |
| 429 | Team request quota or stream capacity exhausted. Respect Retry-After when supplied. |
| 500 | Unexpected server failure. |
| 503 | Required service, quote worker or execution bridge is unavailable. |

### 200 fields

| Field | Type and constraints | Required | Description |
| --- | --- | --- | --- |
| response.success | boolean; one of true | Yes |  |
| response.data | object | Yes |  |
| response.data.scope | string; one of team | Yes |  |
| response.data.unit | string; one of requests | Yes |  |
| response.data.observedAt | string; date-time | Yes |  |
| response.data.minute | object | Yes |  |
| response.data.minute.used | integer; minimum: 0 | Yes |  |
| response.data.minute.resetsAt | string; date-time | Yes |  |
| response.data.month | object | Yes |  |
| response.data.month.used | integer; minimum: 0 | Yes |  |
| response.data.month.resetsAt | string; date-time | Yes |  |

### 400 fields

| Field | Type and constraints | Required | Description |
| --- | --- | --- | --- |
| response.success | boolean; one of false | Yes |  |
| response.error | object | Yes |  |
| response.error.code | string | Yes | Stable machine-readable error code. |
| response.error.message | string | Yes | Human-readable failure description; never contains credentials. |
