# GetRawBookDiffs - Get Book Diff Data

Retrieve order book diffs at a specific position from the Hyperliquid L1 Gateway.

Clone our [gRPC Code Examples Repository](https://github.com/dwellir-public/gRPC-code-examples) for complete, runnable implementations in Go, Python, and Node.js.

## When to Use This Method

`GetRawBookDiffs` is essential for:

- **Targeted Reconstruction** - Pull the book changes for a specific block
- **Historical Analysis** - Retrieve diffs for backtesting or reporting
- **Auditing** - Verify resting-book changes at a specific block
- **Debugging** - Investigate specific book transitions during development

Method signature

```protobuf
rpc GetRawBookDiffs(GetRequest) returns (Record) {}
```

V3 accepts an inclusive `position.block_number` or Unix-millisecond `position.timestamp`; explicit zero is invalid. A positioned call returns the selected envelope. An empty `GetRequest` waits for the next diff record written after the call begins, so set a deadline.

```protobuf
filter: {
  fields: {
    field: "users"
    op: FILTER_OP_IN
    values: "0x1111111111111111111111111111111111111111"
  }
}
```

Raw book diffs accept `users`, `coins` and `dexes` filters. User matching is case-insensitive, coin matching is case-sensitive and dex matching is exact on the HIP-3 dex prefix; values within a field use OR semantics and separate fields use AND semantics. A zero-match unary request returns the selected `Record` with `data` containing `events: []`. A pruned position returns `NOT_FOUND`; malformed filters return `INVALID_ARGUMENT`.

Seed any reconstruction from a snapshot or another known state before applying diffs.

## V3 reference

### Request Parameters

- `position.timestamp` (`int64, optional`): Inclusive V3 Unix-millisecond cursor. Omit the position to wait for the next written record.
- `position.block_number` (`int64, optional`): Inclusive V3 sequential block cursor. Explicit zero is invalid.
- `filter.fields` (`FieldFilter[], optional`): V3 users/coins constraints. Fields AND together; values within one field OR together.
- `filter.fields.field` (`string, required`): Feed-advertised filter field name, either users or coins.
- `filter.fields.op` (`FilterOp, optional`): FILTER_OP_UNSPECIFIED or FILTER_OP_IN.
- `filter.fields.values` (`string[], required`): One or more accepted values; entries within the list use OR semantics.

### Request Example

```bash
grpcurl \
  -import-path content/docs/hyperliquid/grpc \
  -proto v3.proto \
  -H "x-api-key: ${DWELLIR_API_KEY}" \
  -d '{"position":{"block_number":"1058646974"},"filter":{"fields":[{"field":"coins","op":"FILTER_OP_IN","values":["BTC"]}]}}' \
  "${HYPERLIQUID_GRPC_ENDPOINT}" \
  hyperliquid_l1_gateway.v3.HyperliquidL1Gateway/GetRawBookDiffs
```

### Response Fields

- `block_number` (`optional int64, optional`): Sequential Hyperliquid block cursor; never the HyperBFT consensus round.
- `timestamp` (`optional int64, optional`): Record time in Unix milliseconds.
- `data` (`bytes, required`): Bytes from the node_raw_book_diffs_by_block, with non-matching events removed when a filter is applied.

### Successful Response

```protobuf
block_number: 1058646974
timestamp: 1785736814057
data: "{\"block_number\":1058646974,\"events\":[]}"
```

### Error Responses

#### Invalid argument

- Code: `INVALID_ARGUMENT`
- Description: A cursor is zero or malformed, or a filter has an unsupported field, duplicate field, empty name, or empty value list.

#### Not found

- Code: `NOT_FOUND`
- Description: Available replay history varies by feed and endpoint. A cursor outside retained history returns NOT_FOUND.

#### Filter operator not implemented

- Code: `UNIMPLEMENTED`
- Description: The requested filter operator is well formed but is not implemented by this gateway version.

#### Position ahead of head

- Code: `OUT_OF_RANGE`
- Description: The requested block is above the feed's current head and may become available later.

#### Unauthenticated

- Code: `UNAUTHENTICATED`
- Description: The request did not include usable authentication metadata.

#### Permission denied

- Code: `PERMISSION_DENIED`
- Description: The supplied credential is not permitted to call this service.

## V2 reference

### Request Parameters

- `timestamp_ms` (`int64, optional`): Inclusive Unix-millisecond cursor. Omit it to target the latest data.
- `block_height` (`int64, optional`): Inclusive block-height cursor. Omit it to target the latest data.

### Request Example

```bash
grpcurl \
  -import-path content/docs/hyperliquid/grpc \
  -proto v2.proto \
  -H "x-api-key: ${DWELLIR_API_KEY}" \
  -d '{"block_height":"1058646974"}' \
  "${HYPERLIQUID_GRPC_ENDPOINT}" \
  hyperliquid_l1_gateway.v2.HyperliquidL1Gateway/GetRawBookDiffs
```

### Response Fields

- `data` (`bytes, required`): JSON-encoded payload returned in the V2 BlockRawBookDiffs wrapper.

### Successful Response

```protobuf
message BlockRawBookDiffs {
  bytes data = 1;
}
```

```protobuf
rpc GetRawBookDiffs(Position) returns (BlockRawBookDiffs) {}
```

V2 accepts `block_height` or Unix-millisecond `timestamp_ms` and returns the source JSON in `BlockRawBookDiffs.data`.

## Common Use Cases

For V3, select each record with `GetRequest.position.block_number`, decode `Record.data`, and apply the entries in its `events` array. Repeat positioned unary calls when reconciling a bounded block range.

### 1. Inspect a Single Block

```python
def get_block_diffs(client, metadata, block_height):
    request = hyperliquid_pb2.Position(block_height=block_height)
    response = client.GetRawBookDiffs(request, metadata=metadata)
    block = json.loads(response.data)

    for event in block.get('events', []):
        print(event['coin'], event['oid'], event['raw_book_diff'])
```

### 2. Replay a Block Range

```python
def replay_diffs(client, metadata, start_block, end_block):
    for block_height in range(start_block, end_block + 1):
        request = hyperliquid_pb2.Position(block_height=block_height)
        response = client.GetRawBookDiffs(request, metadata=metadata)
        apply(json.loads(response.data))
```

`GetRawBookDiffs` returns the same format as `StreamRawBookDiffs`. See the [StreamRawBookDiffs documentation](https://www.dwellir.com/docs/hyperliquid/stream_raw_book_diffs) for the complete field reference, including the three [`raw_book_diff` shapes](https://www.dwellir.com/docs/hyperliquid/stream_raw_book_diffs#full-book-diff-spec) and [order-identifier keying](https://www.dwellir.com/docs/hyperliquid/stream_raw_book_diffs#order-identifiers).

## Best Practices

1. **Position Selection**: Use the exact sequential block cursor for precise queries; timestamp lookups may resolve to different blocks across requests
2. **Stable Keys**: Index resting orders on `(coin, oid)`, never on `oid` alone
3. **Data Validation**: Always validate JSON structure and branch on the `raw_book_diff` shape
4. **Error Recovery**: Implement retry logic with exponential backoff
5. **Resource Management**: Close gRPC connections properly to avoid resource leaks

## Current Limitations

- **Replay History**: Available replay history varies by feed and endpoint. A cursor outside retained history returns `NOT_FOUND`
- **Rate Limits**: Be mindful of request frequency to avoid overwhelming the service

## Resources

- **[GitHub: gRPC Code Examples](https://github.com/dwellir-public/gRPC-code-examples)** - Complete working examples
- **[StreamRawBookDiffs Documentation](https://www.dwellir.com/docs/hyperliquid/stream_raw_book_diffs)** - Full book diff specification and streaming examples
- **[Historical Data](https://www.dwellir.com/docs/hyperliquid/historical-data)** - Historical `node_raw_book_diffs_by_block` downloads

*Need help? Contact our [support team](mailto:support@dwellir.com) or check the [Hyperliquid gRPC documentation](https://www.dwellir.com/docs/hyperliquid/grpc).*
