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

# DexPaprika DEX API PHP SDK: on-chain liquidity and swap data client

> The official PHP client library for the DexPaprika API, providing easy access to decentralized exchange data across multiple blockchain networks

> [!TIP]
>
> See also: [REST intro](https://docs.dexpaprika.com/api-reference/introduction.md),
> [Networks](https://docs.dexpaprika.com/api-reference/networks/get-a-list-of-available-blockchain-networks.md),
> [Pools](https://docs.dexpaprika.com/api-reference/pools/get-a-pool-on-a-network.md)

## Installation

```bash theme={null}
# Using Composer
composer require coinpaprika/dexpaprika-sdk

# From source
git clone https://github.com/coinpaprika/dexpaprika-sdk-php
cd dexpaprika-sdk-php
composer install
```

## Prerequisites

* PHP 7.4 or higher
* [Composer](https://getcomposer.org/)
* `ext-json` PHP extension
* Connection to the internet to access the DexPaprika API
* No API key needed to start

## PHP SDK quickstart

```php theme={null}
<?php
require_once 'vendor/autoload.php';

use DexPaprika\Client;

// Create client and get WETH price on Ethereum
$client = new Client();
$weth = $client->tokens->getTokenDetails('ethereum', '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2');
echo "{$weth['name']}: \${$weth['summary']['price_usd']}";
// Output: Wrapped Ether: $3245.67
```

## Using an API key (optional)

The SDK works without a key, and that is the default. A [free key](https://console.dexpaprika.com)
raises both the per-minute request limit and the credit allowance. Current figures are on the [rate limits page](https://docs.dexpaprika.com/knowledge-base/rate-limits.md).

Requires v1.8.0 or later.

```php theme={null}
use DexPaprika\Client;
use DexPaprika\Config;

// Explicit
$config = (new Config())->setApiKey('api_your_key_here');
$client = new Client(null, null, false, $config);

// Or leave it out and set DEXPAPRIKA_API_KEY in the environment
$client = new Client();
```

An explicit key beats the environment variable, and no key at all keeps the keyless behaviour
unchanged. The host is never inferred from the key: free keys are served from the default base URL,
and [Dev, Pro and Enterprise](https://docs.dexpaprika.com/api-pro/introduction.md) keys move to `api-pro.dexpaprika.com`.

> [!WARNING]
>
> The key is sent as the entire `Authorization` header value. **Nothing goes in front of it**,
> no scheme word of any kind. The SDK writes the header for you, so this matters when you are
> debugging what went out or calling the API directly. See
> [401 Unauthorized](https://docs.dexpaprika.com/knowledge-base/error-handling.md#401-unauthorized).

## API methods reference

> [!NOTE]
>
> Parameters marked with an asterisk (\*) are required.

**Networks** `category`

**methods**

### client->networks->getNetworks()

**Endpoint:** [GET `/networks`](https://docs.dexpaprika.com/api-reference/networks/get-a-list-of-available-blockchain-networks)

Gets all supported blockchain networks including Ethereum, Solana, etc.

**Parameters:** None

**Returns:** Network IDs, names, and related information. [Response Structure](https://docs.dexpaprika.com/api-reference/networks/get-a-list-of-available-blockchain-networks.md).

```php theme={null}
// Get all networks
$networks = $client->networks->getNetworks();
echo "Found " . count($networks) . " networks";
```

### Finding a network by id

`getNetworks()` returns a plain list of networks, so look one up by its `id`.

```php theme={null}
// Find Ethereum in the network list
foreach ($client->networks->getNetworks() as $network) {
    if ($network['id'] === 'ethereum') {
        echo "Found network: {$network['display_name']}";
    }
}
```

**DEXes** `category`

**methods**

### client->dexes->getNetworkDexes(networkId, options)

**Endpoint:** [GET `/networks/{network}/dexes`](https://docs.dexpaprika.com/api-reference/dexes/get-a-list-of-available-dexes-on-a-network)

Gets all DEXes on a specific network.

**Parameters:**

* `networkId`\* - ID of the network (e.g., 'ethereum', 'solana')
* `options` - Additional options:
  * `page` - Page number for pagination (starts at 0)
  * `limit` - Number of results per page

**Returns:** DEX IDs, names, pool counts, and volume information. [Response Structure](https://docs.dexpaprika.com/api-reference/dexes/get-a-list-of-available-dexes-on-a-network.md).

```php theme={null}
// Get all DEXes on Ethereum
$dexes = $client->dexes->getNetworkDexes('ethereum');
echo "Found " . count($dexes['dexes']) . " DEXes on Ethereum";
```

### Finding a DEX by id

Match on `dex_id`, the value `getDexPools` expects. `dex_name` is the display name.

```php theme={null}
// Find Uniswap V3 on Ethereum
$dexes = $client->dexes->getNetworkDexes('ethereum');
foreach ($dexes['dexes'] as $dex) {
    if ($dex['dex_id'] === 'uniswap_v3') {
        echo "Found DEX: {$dex['dex_name']}";
    }
}
```

**Pools** `category`

**methods**

### client->pools->getTopPools(options)

> [!WARNING]
>
> **The `GET /pools` endpoint was removed and returns `410 Gone`.** Whatever this
> method does locally, the request cannot succeed. Use the network-scoped method
> below and pass a network, or call `GET /pools/search` with a `chains` filter
> directly. See [pool filtering](https://docs.dexpaprika.com/tutorials/pool-filtering.md).

**Endpoint:** GET `/pools` (removed, `410 Gone`)

Gets top pools across all networks with pagination.

**Parameters:**

* `options` - Additional options:
  * `page` - Page number for pagination (starts at 0)
  * `limit` - Number of results per page
  * `orderBy` - Field to sort by ('volume\_usd', 'liquidity\_usd', etc.)
  * `sort` - Sort direction ('asc' or 'desc')

### client->pools->getNetworkPools(networkId, options)

**Endpoint:** [GET `/networks/{network}/pools/search`](https://docs.dexpaprika.com/api-reference/pools/advanced-pool-filtering-on-a-specific-network)

Gets pools on a specific network with pagination and sorting options.

**Parameters:**

* `networkId`\* - ID of the network
* `options` - Additional options:
  * `limit` - Number of results per page
  * `orderBy` - Field to sort by ('volume\_usd\_24h', 'liquidity\_usd', 'txns\_24h', and the rest of the canonical list). Legacy values are mapped, so `volume_usd` is sent as `order_by=volume_usd_24h`, and REST rejects the legacy spelling with `400`
  * `sort` - Sort direction ('asc' or 'desc')
  * `cursor` - Cursor for the next page, taken from `next_cursor` on the previous response. There is no `page` option: the endpoint is cursor-paginated

**Returns:** Cursor-paginated pools for the given network: a `results` array plus `has_next_page` and `next_cursor`. [Response Structure](https://docs.dexpaprika.com/api-reference/pools/advanced-pool-filtering-on-a-specific-network.md).

```php theme={null}
// Get top 5 pools on Ethereum by volume
$pools = $client->pools->getNetworkPools('ethereum', [
    'limit' => 5,
    'orderBy' => 'volume_usd_24h',
    'sort' => 'desc'
]);
echo "Found " . count($pools['results']) . " pools on Ethereum";
```

### client->dexes->getDexPools(networkId, dexId, options)

> [!WARNING]
>
> **`GET /networks/{network}/dexes/{dex}/pools` was removed and returns `410 Gone`.**
> This method now calls `GET /networks/{network}/pools/search?dex_name=...` instead. The DEX id
> moved out of the path and into a filter, so `page` is gone and the response shape changed.
> See [pool filtering](https://docs.dexpaprika.com/tutorials/pool-filtering.md).

**Endpoint:** [GET `/networks/{network}/pools/search?dex_name=...`](https://docs.dexpaprika.com/api-reference/pools/advanced-pool-filtering-on-a-specific-network)

Gets pools on a specific DEX within a network.

**Parameters:**

* `networkId`\* - ID of the network
* `dexId`\* - ID of the DEX, the `dex_id` field from `getNetworkDexes`, case-insensitive (a display name like `Uniswap V3` returns no rows instead of an error)
* `options` - Additional options:
  * `limit` - Number of results per page (max 100)
  * `orderBy` - Field to sort by ('volume\_usd\_24h', 'liquidity\_usd', 'txns\_24h', and the rest of the canonical list)
  * `sort` - Sort direction ('asc' or 'desc')
  * `cursor` - Cursor for the next page, taken from `next_cursor` on the previous response

> [!NOTE]
>
> There is no `page` option any more. The SDK normalizes legacy sort values, so `volume_usd` is
> sent as `order_by=volume_usd_24h`; REST rejects the legacy spelling with a `400` that lists the
> values it will take.

**Returns:** A `results` array plus `has_next_page` and `next_cursor` for cursor pagination. [Response Structure](https://docs.dexpaprika.com/api-reference/pools/advanced-pool-filtering-on-a-specific-network.md).

```php theme={null}
// Get the busiest Uniswap V3 pools on Ethereum
$uniswapPools = $client->dexes->getDexPools('ethereum', 'uniswap_v3', [
    'limit' => 10, 
    'orderBy' => 'volume_usd_24h', 
    'sort' => 'desc'
]);

foreach ($uniswapPools['results'] as $pool) {
    echo "{$pool['dex_name']}: $" . number_format($pool['volume_usd_24h'], 2) . " 24h volume\n";
}
```

### client->pools->getPoolDetails(networkId, poolAddress, options)

**Endpoint:** [GET `/networks/{network}/pools/{pool_address}`](https://docs.dexpaprika.com/api-reference/pools/get-a-pool-on-a-network)

Gets detailed information about a specific pool.

**Parameters:**

* `networkId`\* - ID of the network
* `poolAddress`\* - On-chain address of the pool
* `options` - Additional options:
  * `inversed` - Whether to invert the price ratio (boolean)

**Returns:** Detailed pool information including tokens, volumes, liquidity, and more. [Response Structure](https://docs.dexpaprika.com/api-reference/pools/get-a-pool-on-a-network.md).

```php theme={null}
// Get details for a specific pool (WETH/USDC on Uniswap V2)
$pool = $client->pools->getPoolDetails(
    'ethereum', 
    '0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc', 
    ['inversed' => false]
);
echo "Pool: {$pool['tokens'][0]['symbol']}/{$pool['tokens'][1]['symbol']}";
```

### client->pools->getPoolTransactions(networkId, poolAddress, options)

**Endpoint:** [GET `/networks/{network}/pools/{pool_address}/transactions`](https://docs.dexpaprika.com/api-reference/pools/get-transactions-of-a-pool-on-a-network-paging-can-be-used-up-to-100-pages)

Gets transaction history for a specific pool with pagination.

Needs a Dev, Pro or Enterprise key and the `https://api-pro.dexpaprika.com` base URL since 30 September 2026; keyless and free keys get `403`. See [pool transactions](https://docs.dexpaprika.com/api-reference/pools/get-transactions-of-a-pool-on-a-network-paging-can-be-used-up-to-100-pages.md).

**Parameters:**

* `networkId`\* - ID of the network
* `poolAddress`\* - On-chain address of the pool
* `options` - Additional options:
  * `page` - Page number for pagination
  * `limit` - Number of transactions per page
  * `cursor` - Transaction ID used for cursor-based pagination
  * `from` / `to` - Time window: a relative offset such as `'-1h'`, Unix seconds, RFC3339 or `YYYY-MM-DD`. `from` is inclusive, `to` exclusive; results go back 7 days at most. Needs 1.10.0 or later: earlier releases did not send these two options

**Returns:** List of transactions with details about tokens, amounts, and timestamps. [Response Structure](https://docs.dexpaprika.com/api-reference/pools/get-transactions-of-a-pool-on-a-network-paging-can-be-used-up-to-100-pages.md).

```php theme={null}
// Get the latest 20 transactions for a pool
$transactions = $client->pools->getPoolTransactions(
    'ethereum',
    '0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc',
    ['limit' => 20]
);
$latest = $transactions['transactions'][0];
echo "Latest transaction: {$latest['id']} at {$latest['created_at']}";

// Only the last hour
$lastHour = $client->pools->getPoolTransactions(
    'ethereum',
    '0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc',
    ['limit' => 20, 'from' => '-1h']
);
```

### client->pools->getPoolOHLCV(networkId, poolAddress, start, options)

**Endpoint:** [GET `/networks/{network}/pools/{pool_address}/ohlcv`](https://docs.dexpaprika.com/api-reference/pools/get-ohlcv-data-for-a-pool-pair)

Gets OHLCV (Open, High, Low, Close, Volume) chart data for a pool.

**Parameters:**

* `networkId`\* - ID of the network
* `poolAddress`\* - On-chain address of the pool
* `start`\* - Start time (relative offset such as `-24h`, ISO date string, YYYY-MM-DD, or Unix timestamp), inside your plan's [history window](https://docs.dexpaprika.com/knowledge-base/rate-limits.md#ohlcv-limits-by-plan)
* `options` - Additional options:
  * `end` - End time (optional)
  * `limit` - Number of data points to return
  * `interval` - Time interval ('1m', '5m', '10m', '15m', '30m', '1h', '6h', '12h', '24h'); without a key only '1h' and up
  * `inversed` - Whether to invert the price ratio (boolean)

**Returns:** Array of OHLCV data points for the specified time range and interval. [Response Structure](https://docs.dexpaprika.com/api-reference/pools/get-ohlcv-data-for-a-pool-pair.md).

```php theme={null}
// Hourly candles for the last 24 hours, which works without a key
$ohlcv = $client->pools->getPoolOHLCV(
    'ethereum',
    '0xb4e16d0168e52d35cacd2c6185b44281ec28c9dc',
    '-24h',
    [
        'interval' => '1h',
        'limit' => 24
    ]
);

echo "Received " . count($ohlcv) . " OHLCV data points";
```

***

### client->pools->getPoolIndicator(networkId, poolAddress, indicator, options)

**Endpoint:** [GET `/networks/{network}/pools/{pool_address}/indicators`](https://docs.dexpaprika.com/api-reference/pools/get-a-technical-indicator-for-a-pool)

Computes RSI, EMA, MACD, Bollinger Bands or VWAP from a pool's candles. Requires SDK 1.12.0 or later.

**Parameters:**

* `networkId`\* - ID of the network
* `poolAddress`\* - On-chain address of the pool
* `indicator`\* - `'rsi'`, `'ema'`, `'macd'`, `'bbands'` or `'vwap'`
* `options` - Additional options:
  * `interval` - Candle interval (`'1m'` to `'24h'`, or `'1d'`); pass it every time
  * `start`, `end`, `limit`, `cursor` - Range and paging, same formats as OHLCV
  * `period` - rsi, ema, bbands and vwap
  * `fast_period`, `slow_period`, `signal_period` - macd only
  * `std_dev` - bbands only

Set only the options the chosen indicator uses: one that belongs to another indicator is refused by the API with a 400.

**Returns:** `data`, oldest first, one point per completed candle: `value` for rsi, ema and vwap; `macd`, `signal` and `histogram` for macd; `upper`, `middle` and `lower` for bbands. `parameters` echoes the settings used, defaults included, and `has_next_page`/`next_cursor` page through longer ranges. [Response Structure](https://docs.dexpaprika.com/api-reference/pools/get-a-technical-indicator-for-a-pool.md).

Works on every plan, keyless included, with the same interval and history limits as pool OHLCV ([limits by plan](https://docs.dexpaprika.com/knowledge-base/rate-limits.md#ohlcv-limits-by-plan)). Tutorial: [technical indicators](https://docs.dexpaprika.com/tutorials/technical-indicators.md).

```php theme={null}
// Hourly RSI over the last 24 hours, which works without a key
$rsi = $client->pools->getPoolIndicator(
    'ethereum',
    '0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640',
    'rsi',
    ['interval' => '1h', 'start' => '-24h']
);

$last = end($rsi['data']);
echo "RSI {$last['value']} at {$last['timestamp']}";
```

**Tokens** `category`

**methods**

### client->tokens->getTokenDetails(networkId, tokenAddress)

**Endpoint:** [GET `/networks/{network}/tokens/{token_address}`](https://docs.dexpaprika.com/api-reference/tokens/get-a-tokens-latest-data-on-a-network)

Gets comprehensive token information.

**Parameters:**

* `networkId`\* - ID of the network
* `tokenAddress`\* - Token contract address

**Returns:** Token details including price, market cap, volume, and metadata. [Response Structure](https://docs.dexpaprika.com/api-reference/tokens/get-a-tokens-latest-data-on-a-network.md).

```php theme={null}
// Get WETH token details
$weth = $client->tokens->getTokenDetails(
    'ethereum',
    '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'
);
echo "{$weth['name']} price: \${$weth['summary']['price_usd']}";
```

### client->tokens->getTokenPools(networkId, tokenAddress, options)

> [!WARNING]
>
> **`GET /networks/{network}/tokens/{token_address}/pools` was removed and returns `410 Gone`.**
> This method now calls `GET /networks/{network}/pools/search?token_address=...` instead. The old
> second-token `address` pair filter and the `reorder` flag have no replacement and are not sent.
> See [pool filtering](https://docs.dexpaprika.com/tutorials/pool-filtering.md).

**Endpoint:** [GET `/networks/{network}/pools/search?token_address=...`](https://docs.dexpaprika.com/api-reference/pools/advanced-pool-filtering-on-a-specific-network)

Gets pools containing a specific token.

**Parameters:**

* `networkId`\* - ID of the network
* `tokenAddress`\* - Token contract address
* `options` - Additional options:
  * `limit` - Number of results per page (max 100)
  * `sort` - Sort direction ('asc' or 'desc')
  * `orderBy` - Field to sort by ('volume\_usd\_24h', 'liquidity\_usd', 'txns\_24h', and the rest of the canonical list)
  * `cursor` - Cursor for the next page, taken from `next_cursor` on the previous response

> [!NOTE]
>
> There is no `page` option and no second-token `address` option. The endpoint is cursor-paginated,
> and `GET /networks/{network}/pools/search` has no pair filter, so anything you pass under those
> names is dropped before the request goes out. Filter `results[].tokens` yourself to match a pair.
> The SDK also normalizes legacy sort values, so `volume_usd` is sent as `order_by=volume_usd_24h`;
> REST rejects the legacy spelling with `400`.

**Returns:** A `results` array plus `has_next_page` and `next_cursor` for cursor pagination. [Response Structure](https://docs.dexpaprika.com/api-reference/pools/advanced-pool-filtering-on-a-specific-network.md).

```php theme={null}
// Get the busiest WETH pools on Ethereum
$pools = $client->tokens->getTokenPools(
    'ethereum',
    '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',  // WETH
    [
        'limit' => 5,
        'orderBy' => 'volume_usd_24h',
        'sort' => 'desc'
    ]
);

foreach ($pools['results'] as $pool) {
    echo "{$pool['dex_name']}: $" . number_format($pool['volume_usd_24h'], 2) . " 24h volume\n";
}
```

### client->tokens->findToken(networkId, tokenAddress)

**Endpoint:** [GET `/networks/{network}/tokens/{token_address}`](https://docs.dexpaprika.com/api-reference/tokens/get-a-tokens-latest-data-on-a-network)

Same call as `getTokenDetails`, wrapped so a missing token raises
`NotFoundException` instead of handing you an empty payload to check.

**Parameters:**

* `networkId`\* - ID of the network
* `tokenAddress`\* - Contract address of the token
* `asObject` - Return an object instead of an array (default: `false`)

**Returns:** Token details, the same shape as `getTokenDetails`.

```php theme={null}
// Find WETH by its contract address
try {
    $token = $client->tokens->findToken(
        'ethereum',
        '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2'
    );
    echo "Found {$token['name']} at {$token['id']}";
} catch (DexPaprika\Exception\NotFoundException $e) {
    echo "Token not found";
}
```

***

### client->tokens->getTokenIndicator(networkId, tokenAddress, indicator, options)

**Endpoint:** [GET `/networks/{network}/tokens/{token_address}/indicators`](https://docs.dexpaprika.com/api-reference/tokens/get-a-technical-indicator-for-a-token)

The same indicators computed from the token's USD candles across every pool it trades in. Same options and response shape as `getPoolIndicator`. Requires SDK 1.12.0 or later.

Needs a Dev, Pro or Enterprise key and the `https://api-pro.dexpaprika.com` base URL; keyless and free keys get `403`, thrown as a `ClientException` whose `getErrorData()['required_tier']` names the plan.

```php theme={null}
$macd = $client->tokens->getTokenIndicator(
    'ethereum',
    '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2', // WETH
    'macd',
    ['interval' => '1h', 'start' => '-7d']
);
```

**Search** `category`

**methods**

### client->search->search(query)

**Endpoint:** [GET `/search`](https://docs.dexpaprika.com/api-reference/search/search-for-tokens-pools-and-dexes)

Searches across tokens, pools, and DEXes using a query string.

**Parameters:**

* `query`\* - Search query string

**Returns:** Matching entities from all categories (tokens, pools, DEXes). [Response Structure](https://docs.dexpaprika.com/api-reference/search/search-for-tokens-pools-and-dexes.md).

```php theme={null}
// Search for "ethereum" across all entities
$results = $client->search->search("ethereum");

echo "Found " . count($results['tokens']) . " tokens\n";
echo "Found " . count($results['pools']) . " pools\n";
echo "Found " . count($results['dexes']) . " dexes\n";
```

**Stats** `category`

**methods**

### client->stats->getStats()

**Endpoint:** [GET `/stats`](https://docs.dexpaprika.com/api-reference/utils/retrieve-high-level-asset-statistics)

Gets platform-wide statistics.

**Parameters:** None

**Returns:** Counts of chains, DEXes, pools, and tokens indexed. [Response Structure](https://docs.dexpaprika.com/api-reference/utils/retrieve-high-level-asset-statistics.md).

```php theme={null}
// Get platform statistics
$stats = $client->stats->getStats();

echo "Total chains: {$stats['chains']}\n";
echo "Total DEX factories: {$stats['factories']}\n";
echo "Total pools: {$stats['pools']}\n";
echo "Total tokens: {$stats['tokens']}\n";
```

## Complete example

**example**

```php theme={null}
<?php

require_once __DIR__ . '/vendor/autoload.php';

use DexPaprika\Client;
use DexPaprika\Exception\DexPaprikaApiException;

const WETH = '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2';
const USDC = '0xa0b86991c6218b36c1d19d4a2e9eb0ce3606eb48';

function main() {
    try {
        $client = new Client();

        // Networks come back as a plain list of ['id' => ..., 'display_name' => ...]
        $networks = $client->networks->getNetworks();
        foreach ($networks as $network) {
            if ($network['id'] === 'ethereum') {
                echo "{$network['display_name']} is one of " . count($networks) . " networks\n";
            }
        }

        // Price lives under summary
        $weth = $client->tokens->getTokenDetails('ethereum', WETH);
        echo "{$weth['name']} price: $" . number_format($weth['summary']['price_usd'], 2) . "\n";

        // Pools for a token are served by pool search. Rows sit under results,
        // sorted by volume_usd_24h. There is no pair filter, so match the second
        // token client-side.
        $pools = $client->tokens->getTokenPools('ethereum', WETH, [
            'limit' => 20,
            'orderBy' => 'volume_usd_24h',
            'sort' => 'desc',
        ]);

        echo "Top WETH/USDC pools:\n";
        $shown = 0;
        foreach ($pools['results'] as $pool) {
            $ids = array_column($pool['tokens'], 'id');
            if (in_array(USDC, $ids, true) && $shown < 5) {
                echo "{$pool['dex_name']}: $" . number_format($pool['volume_usd_24h'], 2) . " 24h volume\n";
                $shown++;
            }
        }
    } catch (DexPaprikaApiException $e) {
        echo "API Error: {$e->getMessage()}\n";
    }
}

main();
```

## Advanced features

### Error handling

**error handling**

```php theme={null}
<?php

require_once __DIR__ . '/vendor/autoload.php';

use DexPaprika\Client;
use DexPaprika\Exception\DexPaprikaApiException;
use DexPaprika\Exception\NotFoundException;
use DexPaprika\Exception\RateLimitException;
use DexPaprika\Exception\ServerException;
use DexPaprika\Exception\NetworkException;

// Basic error handling
try {
    $client = new Client();
    $token = $client->tokens->getTokenDetails('ethereum', '0xinvalidaddress');
} catch (NotFoundException $e) {
    echo "Token not found: {$e->getMessage()}\n";
} catch (RateLimitException $e) {
    echo "Rate limit exceeded: {$e->getMessage()}\n";
} catch (ServerException $e) {
    echo "Server error occurred: {$e->getMessage()}\n";
} catch (NetworkException $e) {
    echo "Network error: {$e->getMessage()}\n";
} catch (DexPaprikaApiException $e) {
    echo "API error: {$e->getMessage()} (Code: {$e->getCode()})\n";
} catch (Exception $e) {
    echo "General error: {$e->getMessage()}\n";
}

// The SDK automatically retries 429 (Too Many Requests), any 5xx
// (server errors) and connection failures

// Custom retry configuration
use DexPaprika\Config;

$config = new Config();
$config->setMaxRetries(4);  // Number of retry attempts (default: 5)
$config->setRetryDelays([100, 500, 1000, 5000]);  // Delay in milliseconds

$client = new Client(null, null, false, $config);

// All API requests will now use these retry settings
try {
    $networks = $client->networks->getNetworks();
} catch (Exception $e) {
    echo "Failed after multiple retries: {$e->getMessage()}\n";
}
```

### Caching system

**caching**

```php theme={null}
<?php

require_once __DIR__ . '/vendor/autoload.php';

use DexPaprika\Client;
use DexPaprika\Cache\FilesystemCache;

// The SDK includes built-in caching capabilities
// Demonstration of cache behavior

// Create a client with default cache (1 hour TTL)
$client = new Client();
$client->setupCache();

// First call - hits the API
$startTime = microtime(true);
$networks = $client->networks->getNetworks();
$firstCallTime = microtime(true) - $startTime;
echo "First call (API): " . count($networks) . " networks, took {$firstCallTime} s\n";

// Second call - served from cache (much faster)
$startTime = microtime(true);
$networks = $client->networks->getNetworks();
$secondCallTime = microtime(true) - $startTime;
echo "Second call (cached): " . count($networks) . " networks, took {$secondCallTime} s\n";
echo "Cache speedup: " . ($firstCallTime / $secondCallTime) . "x\n";

// You can skip the cache when you need fresh data
$client->getConfig()->setCacheEnabled(false);
$freshNetworks = $client->networks->getNetworks();
$client->getConfig()->setCacheEnabled(true);

// Custom cache configuration
$cache = new FilesystemCache('/path/to/custom/cache');
$client->setupCache($cache, 300);  // 5 minutes TTL

// Clear the entire cache
$client->getConfig()->getCache()->clear();
```

### Pagination helper

**pagination**

```php theme={null}
<?php

require_once __DIR__ . '/vendor/autoload.php';

use DexPaprika\Client;

function fetchAllPools($networkId, $maxPages = 10) {
    $client = new Client();
    $allPools = [];
    $limit = 50;
    $cursor = null;
    
    // Every page is one request against your allowance, so cap the walk
    for ($page = 0; $page < $maxPages; $page++) {
        $options = [
            'limit' => $limit,
            'sort' => 'desc',
            'orderBy' => 'volume_usd_24h'
        ];
        if ($cursor !== null) {
            $options['cursor'] = $cursor;
        }
        
        $response = $client->pools->getNetworkPools($networkId, $options);
        
        $allPools = array_merge($allPools, $response['results']);
        
        // The search endpoint is cursor-paginated, not page-numbered
        if (empty($response['has_next_page']) || empty($response['next_cursor'])) {
            break;
        }
        $cursor = $response['next_cursor'];
        
        // Keyless allows 15 requests a minute, so pause 4 seconds between pages.
        // A free key doubles the rate: https://console.dexpaprika.com
        sleep(4);
    }
    
    echo "Fetched a total of " . count($allPools) . " pools on {$networkId}\n";
    return $allPools;
}

// Alternative approach using built-in paginator
function fetchAllPoolsWithPaginator($networkId) {
    $client = new Client();
    $allPools = [];
    
    // Create a paginator for network pools. Arguments are keyed by the
    // method's parameter names; whatever is left becomes the options array.
    $paginator = $client->createPaginator(
        $client->pools,
        'getNetworkPools',
        [
            'networkId' => $networkId,
            'limit' => 50,
            'orderBy' => 'volume_usd_24h',
            'sort' => 'desc'
        ]
    );
    
    // Paginator is not Traversable and has no setMaxPages(), so walk it explicitly.
    $page = 0;
    while ($paginator->hasNextPage() && $page < 10) {
        $response = $paginator->getNextPage();
        $allPools = array_merge($allPools, $response['results']);
        echo "Processed page {$page}, got " . count($response['results']) . " pools\n";
        $page++;
    }
    
    echo "Fetched a total of " . count($allPools) . " pools on {$networkId}\n";
    return $allPools;
}

// Usage example
$ethereumPools = fetchAllPools('ethereum');
// or
$ethereumPools = fetchAllPoolsWithPaginator('ethereum');
```

### Working with objects

**objects**

```php theme={null}
<?php

require_once __DIR__ . '/vendor/autoload.php';

use DexPaprika\Client;

$client = new Client();

// Pass 'asObject' => true to get an object instead of an array
$pool = $client->pools->getPoolDetails(
    'ethereum',
    '0x88e6a0c2ddd26feeb64f039a2c41296fcb3f5640',  // USDC/WETH Uniswap v3 pool
    ['asObject' => true]
);

// Access pool properties as object properties
echo "Pool: {$pool->tokens[0]->symbol}/{$pool->tokens[1]->symbol}\n";
echo "Price: \${$pool->last_price_usd}\n";

// Time windows are keyed '24h', '6h', '1h', '30m', '15m' and '5m', so use
// the brace syntax to read them
echo "Volume (24h): \${$pool->{'24h'}->volume_usd}\n";
echo "Transactions (24h): {$pool->{'24h'}->txns}\n";
echo "1h price change: {$pool->{'1h'}->last_price_usd_change}%\n";
echo "24h price change: {$pool->{'24h'}->last_price_usd_change}%\n";

// Calls without the option keep returning arrays
$networks = $client->networks->getNetworks();

$token = $client->tokens->getTokenDetails(
    'ethereum',
    '0xc02aaa39b223fe8d0a0e5c4f27ead9083c756cc2',
    ['asObject' => true]
);
echo "{$token->name} price: \${$token->summary->price_usd}\n";
```

## Resources

* [GitHub Repository](https://github.com/coinpaprika/dexpaprika-sdk-php)
* [DexPaprika Website](https://dexpaprika.com)
* [API Reference](https://docs.dexpaprika.com/api-reference/introduction.md)
* [Discord Community](https://discord.gg/DhJge5TUGM)

## API status

The DexPaprika API provides consistent data with stable endpoints. Requests are answered without an API key on the free tier, and a free key raises the credit allowance. We aim to maintain backward compatibility and provide notice of any significant changes.

### FAQs

> **Do I need an API key with this SDK?**
>
> Not to start. Keyless requests work at 10,000 credits per rolling 30 days per IP, and a [free registered key](https://console.dexpaprika.com/dashboard) raises that to 100,000 with no card.

> **How do I find identifiers?**
>
> Use Coverage Checker or list Networks and query Tokens/Pools to discover addresses.

> **How do I get historical or transactions data?**
>
> Use pools/transactions endpoints with `pool_address`, `network`, and time/paging params as documented. Transactions need a Dev, Pro or Enterprise key on `api-pro.dexpaprika.com`.

> **What about rate limiting?**
>
> 15 requests a minute keyless, 30 with a [free key](https://console.dexpaprika.com) and 500 on Pro, against 10,000 credits keyless and 100,000 with a free key over a rolling 30 days, or 5,000,000 a month on Pro. Retry transient HTTP errors with backoff. See [rate limits](https://docs.dexpaprika.com/knowledge-base/rate-limits.md) for how the counters work, and [Pro pricing](https://dexpaprika.com/api/pricing) for what the 5,000,000 credit tier costs.


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