> For the complete documentation index, see [llms.txt](https://docs.hydromancer.xyz/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.hydromancer.xyz/readme/rest-api/user-performance/usercompletedtradesbytime.md).

# userCompletedTradesByTime

Returns completed trades for a user with `close_time` ≥ `startTime` (or after a `cursor`), ordered **oldest-first** by `close_time` then `tx_index`. Pagination is identical to [`userFillsByTime`](/readme/rest-api/historical-data/userfillsbytime.md): pass `startTime` for the first page, then on each subsequent page pass the `cursor` `"{close_time}_{tx_index}"` built from the **last record** of the previous response. When `cursor` is supplied, `startTime` is ignored and the boundary record is excluded from the next page (no duplicates, no `+ 1` hack).

`dex` is optional. Omit to return trades from every perp DEX. Set to `"main_dex"` for the native Hyperliquid dex, or to a HIP-3 dex name (e.g. `"xyz"`) to scope the trade list to that dex.

**note: completed-trade data starts from 1st of aug 2025**

## POST Request

<table><thead><tr><th width="161.9998779296875">Field</th><th width="119">Type</th><th>Description</th></tr></thead><tbody><tr><td><code>type</code></td><td>string</td><td>Must be <code>"userCompletedTradesByTime"</code></td></tr><tr><td><code>user</code></td><td>string</td><td>Ethereum address (0x-prefixed, 42 characters)</td></tr><tr><td><code>startTime</code></td><td>integer</td><td>Earliest <code>close_time</code> to include (Unix ms, inclusive). Skipped when <code>cursor</code> is used. Either <code>startTime</code> or <code>cursor</code> must be supplied</td></tr><tr><td><code>endTime</code></td><td>integer</td><td>Latest <code>close_time</code> to include (Unix ms, inclusive). Default: now (optional)</td></tr><tr><td><code>cursor</code></td><td>string</td><td>Composite of <code>close_time</code> and <code>tx_index</code> with <code>"_"</code> separator (e.g. <code>"1734571490123_12"</code>). Take from the <strong>last record</strong> of the previous response (optional)</td></tr><tr><td><code>builder</code></td><td>string</td><td>Filter to trades whose closing fill was routed through this builder address (last-touch attribution, 0x-prefixed). Omit for no filtering (optional)</td></tr><tr><td><code>limit</code></td><td>integer</td><td>Max results to return. Default 100, max 500 (optional)</td></tr><tr><td><code>dex</code></td><td>string</td><td>Scope to a single perp DEX. <code>"main_dex"</code> for the native Hyperliquid dex, or a HIP-3 dex name (e.g. <code>"xyz"</code>). Omit for all dexes (optional)</td></tr></tbody></table>

{% tabs %}
{% tab title="cURL" %}

```bash
# First page
curl -X POST https://api.hydromancer.xyz/info \
  -H "Authorization: Bearer $HYDROMANCER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "userCompletedTradesByTime",
    "user": "0x0000000000000000000000000000000000000000",
    "startTime": 0,
    "limit": 100
  }'

# Next page — cursor built from the last record of the previous response
curl -X POST https://api.hydromancer.xyz/info \
  -H "Authorization: Bearer $HYDROMANCER_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "type": "userCompletedTradesByTime",
    "user": "0x0000000000000000000000000000000000000000",
    "cursor": "1734571490123_12",
    "limit": 100
  }'
```

{% endtab %}

{% tab title="Python" %}

```python
import requests
import os

url = 'https://api.hydromancer.xyz/info'
headers = {
    'Authorization': f'Bearer {os.environ.get("HYDROMANCER_API_KEY")}',
    'Content-Type': 'application/json',
}
user = '0x0000000000000000000000000000000000000000'
limit = 500

params = {'type': 'userCompletedTradesByTime', 'user': user, 'startTime': 0, 'limit': limit}
while True:
    page = requests.post(url, json=params, headers=headers).json()
    if not page:
        break
    for trade in page:
        ...  # process trade
    if len(page) < limit:
        break
    last = page[-1]
    params = {
        'type': 'userCompletedTradesByTime',
        'user': user,
        'cursor': f"{last['close_time']}_{last['tx_index']}",
        'limit': limit,
    }
```

{% endtab %}

{% tab title="Javascript" %}

```javascript
import axios from 'axios';

const url = 'https://api.hydromancer.xyz/info';
const headers = {
    'Authorization': `Bearer ${process.env.HYDROMANCER_API_KEY}`,
    'Content-Type': 'application/json',
};
const user = '0x0000000000000000000000000000000000000000';
const limit = 500;

let params = { type: 'userCompletedTradesByTime', user, startTime: 0, limit };
while (true) {
    const { data: page } = await axios.post(url, params, { headers });
    if (!page.length) break;
    for (const trade of page) { /* process trade */ }
    if (page.length < limit) break;
    const last = page[page.length - 1];
    params = {
        type: 'userCompletedTradesByTime',
        user,
        cursor: `${last.close_time}_${last.tx_index}`,
        limit,
    };
}
```

{% endtab %}
{% endtabs %}

***

## Response Fields

JSON array of completed-trade records, ordered **oldest-first** by `close_time` then `tx_index`. Same record shape as [userCompletedTrades](/readme/rest-api/user-performance/usercompletedtrades.md):

| Field                  | Type     | Description                                                                               |
| ---------------------- | -------- | ----------------------------------------------------------------------------------------- |
| `user`                 | string   | Ethereum address                                                                          |
| `coin`                 | string   | Asset symbol                                                                              |
| `position_type`        | string   | `"Long"` or `"Short"`                                                                     |
| `gross_pnl`            | string   | Realized PnL before fees                                                                  |
| `net_pnl`              | string   | Realized PnL after fees and funding (`gross_pnl - fees + funding_pnl`)                    |
| `funding_pnl`          | string   | Cumulative funding PnL accrued while the position was open. Already included in `net_pnl` |
| `entry_px`             | string   | Volume-weighted average entry price                                                       |
| `exit_px`              | string   | Volume-weighted average exit price                                                        |
| `position_closed_size` | string   | Size of the closed position, in contracts                                                 |
| `fees`                 | string   | Total fees paid on fills attributed to this position                                      |
| `fills`                | int      | Number of fills on this position                                                          |
| `maker_fills`          | int      | Number of uncrossed (maker) fills on this position                                        |
| `open_time`            | int      | Time the position was opened (Unix ms)                                                    |
| `close_time`           | int      | Time the position was fully closed (Unix ms) — **first half of the next-page `cursor`**   |
| `duration_ms`          | int      | `close_time - open_time` in milliseconds                                                  |
| `max_position_size`    | string   | Peak absolute position size during the position's lifetime                                |
| `builder`              | string?  | Builder address from the closing fill, if any                                             |
| `tx_index`             | int      | Transaction index of the closing fill — **second half of the next-page `cursor`**         |
| `leverage`             | int?     | Leverage at close. `null` if predates leverage tracking                                   |
| `is_cross`             | boolean? | `true` = cross margin, `false` = isolated. `null` when `leverage` is null                 |
