Table of contents

LP Intelligence Documentation

1. Overview

1.1 What is LP Intelligence

LP Intelligence is a structured data layer for DeFi liquidity protocols.

It provides indexed, queryable access to liquidity positions, swaps, pools, and fee state without requiring teams to build and maintain custom on-chain indexers.

The API abstracts raw on-chain events into protocol-aware data models designed for:

  • DeFi dashboards and frontends
  • Vault and strategy builders
  • Analytics and research teams
  • Internal data platforms

The goal is to reduce integration time from months of indexing and ETL work to days of API integration.

1.2 Supported Protocols

LP Intelligence currently supports:

  • Uniswap v4 (initial integration)

The architecture is designed for multi-protocol expansion. Future integrations will follow the same structured data model principles to ensure consistency across protocols.

1.3 Current Beta Status

LP Intelligence is currently in Public Beta.

Current characteristics:

  • Read-only API
  • Historical indexing available
  • Data freshness may lag (see Data Freshness section)
  • API access granted via request

Uniswap v4 hooks (current limitation):
LP Intelligence currently indexes core Uniswap v4 protocol contracts (e.g., PoolManager) and does not yet interpret or model hook-specific logic.

If a pool uses hooks that alter swap or liquidity behavior, derived fields may not fully reflect hook-level custom semantics. The hooks address is exposed as metadata when available.

Near real-time ingestion, access tiers, and additional protocol coverage are planned in upcoming phases.

1.4 Design Principles

LP Intelligence is built with the following principles:

Deterministic indexing

All responses are derived from deterministic indexing pipelines. Data is processed in strict block order to ensure consistency and reproducibility.

Structured, protocol-aware models

Rather than exposing raw contract events, the API provides:

  • Normalized pool objects
  • Structured position models
  • Canonical token metadata
  • Computed fee state

This reduces client-side transformation logic and simplifies integration.

Transparent data freshness

Each response includes indexing metadata:

  • indexed_to_block — the highest block included in the dataset
  • indexed_at — the timestamp when indexing completed

This allows clients to reason about freshness and build their own consistency guarantees.

Developer-first API design

  • Predictable pagination (limit, next_cursor)
  • Consistent response envelopes
  • Clear error formats
  • Versioned endpoints

The API is designed for integration into production systems.

2. Quickstart

This section demonstrates how to make your first request to the LP Intelligence API and understand the response structure.

The API is REST-based and returns JSON responses.

2.1 Base URL

All requests should be sent to:

https://api.blockforest.xyz

All endpoints are versioned. The current version is:

/api/v1

Example full URL:

https://api.blockforest.xyz/api/v1/pools

2.2 Authentication

During Public Beta, API access is granted via API key.

Requests must include your API key in the Authorization header:

Authorization: Bearer YOUR_API_KEY

Example:

curl https://api.blockforest.xyz/api/v1/pools \
  -H "Authorization: Bearer YOUR_API_KEY"

If authentication fails, the API will return 401 Unauthorized.

2.3 First Request Example

The following example retrieves a list of indexed pools on Ethereum mainnet (chain_id=1).

curl "https://api.blockforest.xyz/api/v1/pools?chain_id=1&limit=10" \
  -H "Authorization: Bearer YOUR_API_KEY"

Query parameters:

  • chain_id — numeric chain identifier (e.g., 1 for Ethereum mainnet)
  • limit — number of results to return (max value defined by rate limits)

2.4 Example Response

A successful response returns HTTP 200 OK and a JSON body:

{
    "indexed_to_block": 21955247,
    "indexed_at": "2025-03-01T23:16:47Z",
    "items": [
        {
            "chain_id": 1,
            "pool_id": "0xfa86f15833604c1ee16f0a2f7de9d04d244fd9248a3ffe71220f7718f21871fe",
            "pool_manager": "0x000000000004444c5dc75cb358380d2e3de08a90",
            "fee": 100,
            "tick_spacing": 1,
            "hooks": null,
            "created_block": 21954731,
            "created_timestamp": "2025-03-01T21:33:23+00:00",
            "token0": {
                "address": "0x6111f3e70fbe68c8d62c3340b32e978ddfd8b08a",
                "symbol": "SETH",
                "decimals": 18,
                "name": "Sethereum"
            },
            "token1": {
                "address": "0xdb99b0477574ac0b2d9c8cec56b42277da3fdb82",
                "symbol": "DECT",
                "decimals": 18,
                "name": "DEC Token"
            }
        },
        {
            "chain_id": 1,
            "pool_id": "0x08d3552d8874b93fdbd8947916c89a375b3c96c34fb49cdf5b8ca79aef020b5c",
            "pool_manager": "0x000000000004444c5dc75cb358380d2e3de08a90",
            "fee": 10000,
            "tick_spacing": 200,
            "hooks": null,
            "created_block": 21954709,
            "created_timestamp": "2025-03-01T21:28:59+00:00",
            "token0": {
                "address": "0x0000000000000000000000000000000000000000",
                "symbol": "ETH",
                "decimals": 18,
                "name": "Ether"
            },
            "token1": {
                "address": "0x940a2db1b7008b6c776d4faaca729d6d4a4aa551",
                "symbol": "DUSK",
                "decimals": 18,
                "name": "Dusk Network"
            }
        },
        ...
        {
            "chain_id": 1,
            "pool_id": "0xe16b9af685b248d4f3e54890353f6f5086a8608d05ed806c4876ce99d4e6c12a",
            "pool_manager": "0x000000000004444c5dc75cb358380d2e3de08a90",
            "fee": 3000,
            "tick_spacing": 60,
            "hooks": null,
            "created_block": 21953412,
            "created_timestamp": "2025-03-01T17:08:35+00:00",
            "token0": {
                "address": "0x0000000000000000000000000000000000000000",
                "symbol": "ETH",
                "decimals": 18,
                "name": "Ether"
            },
            "token1": {
                "address": "0xb9f599ce614feb2e1bbe58f180f370d05b39344e",
                "symbol": "PORK",
                "decimals": 18,
                "name": "PepeFork"
            }
        },
        {
            "chain_id": 1,
            "pool_id": "0x4a24fcf3c1cbebd80d09537c7aa15adf2d228aa1b9ba1b0e5fb82e66dc96c0d2",
            "pool_manager": "0x000000000004444c5dc75cb358380d2e3de08a90",
            "fee": 500,
            "tick_spacing": 10,
            "hooks": null,
            "created_block": 21953133,
            "created_timestamp": "2025-03-01T16:12:47+00:00",
            "token0": {
                "address": "0x2260fac5e5542a773aa44fbcfedf7c193bc2c599",
                "symbol": "WBTC",
                "decimals": 8,
                "name": "Wrapped BTC"
            },
            "token1": {
                "address": "0xdac17f958d2ee523a2206206994597c13d831ec7",
                "symbol": "USDT",
                "decimals": 6,
                "name": "Tether USD"
            }
        }
    ],
    "next_cursor": "eyJjYiI6MjE5NTMxMzMsInBpZCI6IjB4NGEyNGZjZjNjMWNiZWJkODBkMDk1MzdjN2FhMTVhZGYyZDIyOGFhMWI5YmExYjBlNWZiODJlNjZkYzk2YzBkMiJ9",
    "limit": 10
}

Response envelope

All list endpoints return a consistent response envelope:

  • indexed_to_block — highest indexed block included in the dataset
  • indexed_at — timestamp when indexing completed
  • items — array of result objects
  • next_cursor — pagination cursor (if more results exist)
  • limit — number of items requested

This structure is consistent across endpoints.

2.5 Pagination Basics

List endpoints support cursor-based pagination.

To retrieve additional results:

  1. Inspect the next_cursor field in the response.
  2. If not null, pass it as a query parameter in the next request.

Example:

curl "https://api.blockforest.xyz/api/v1/pools?chain_id=1&limit=10&cursor=eyJvZmZzZXQiOjEwfQ==" \
  -H "Authorization: Bearer YOUR_API_KEY"

Pagination guarantees:

  • Results are returned in deterministic order.
  • next_cursor ensures no overlap between pages.
  • If next_cursor is null, no additional results are available.

3. Authentication

LP Intelligence uses API key–based authentication for all requests.

Authentication follows a server-to-server model designed for backend integration. Each request must include a valid API key in the Authorization header.

API keys are long-lived, revocable, and scoped to a single account.

3.1 API Keys

Access to LP Intelligence is granted via API key.

Each account is issued a unique secret key during onboarding.

Example key format:

bf_live_7f4c9a2e3d1b4f0c8a6d9e2f1b3c4d5e

API keys:

  • Are account-scoped
  • Can be revoked or rotated
  • Should be kept secret
  • Must never be exposed in client-side code

Security recommendations

  • Store API keys in secure server-side environment variables.
  • Do not embed keys in frontend applications.
  • Rotate keys if you suspect compromise.

If a key is revoked, all subsequent requests using that key will return 401 Unauthorized.

3.2 Request Headers

All authenticated requests must include the Authorization header:

Authorization: Bearer YOUR_API_KEY

Example request:

curl https://api.blockforest.xyz/api/v1/pools \
  -H "Authorization: Bearer bf_live_7f4c9a2e3d1b4f0c8a6d9e2f1b3c4d5e"

Requests without a valid API key will receive:

401 Unauthorized

If the key is valid but exceeds rate limits, the API will return:

429 Too Many Requests

3.3 Rate Limits

Rate limits are enforced per API key.

Limits are defined based on your access tier.

Rate limiting ensures:

  • Fair resource allocation
  • System stability
  • Predictable performance

When a rate limit is exceeded, the API responds with:

429 Too Many Requests

Example response:

{
  "error": "rate_limit_exceeded",
  "message": "Request rate limit exceeded. Please retry later."
}

Future versions may include rate limit headers such as:

  • X-RateLimit-Limit
  • X-RateLimit-Remaining
  • X-RateLimit-Reset

Rate limits may be adjusted during Public Beta.

3.4 Access Tiers (Beta / Future Plans)

LP Intelligence currently operates under a Public Beta tier.

Public Beta

  • Read-only access
  • Historical indexing
  • Controlled rate limits
  • Access granted via request

Planned Tiers

Future access tiers may include:

  • Higher request limits
  • Near real-time ingestion access
  • Dedicated rate limits
  • Priority support
  • Advanced features (e.g., profitability layer, webhooks, batch endpoints)

Access tier and limits are determined at the account level.

Details on paid tiers will be published prior to general availability.

4. Core Concepts

This section defines the core data models used throughout LP Intelligence.

Rather than exposing raw contract events, the API provides structured, protocol-aware objects designed for integration into production systems.

4.1 Chains

A Chain represents a supported blockchain network.

Each chain is identified by a numeric chain_id, consistent with EVM standards.

Example:

  • 1 — Ethereum mainnet
  • (additional chains will be added in future phases)

Chain-level context determines:

  • Address namespace
  • Block height
  • Indexing boundaries
  • Protocol deployment addresses

All endpoints that return chain-specific data include a chain_id field.

4.2 Pools

A Pool represents a liquidity pool within a supported protocol.

Pools define:

  • pool_id — unique identifier
  • token0 and token1 — assets paired in the pool
  • fee — fee tier
  • tick_spacing — discrete price granularity
  • Protocol-specific metadata (e.g., hooks in Uniswap v4)

Pools are canonicalized in the API to provide:

  • Normalized token metadata
  • Structured fee configuration
  • Deterministic identification

Pools serve as the foundational market unit for swaps and positions.

Hooks Support (Uniswap v4)

  • The hooks address is exposed as metadata when available.
  • LP Intelligence does not currently decode or model hook callback logic.
  • Indexed data reflects core protocol events and state; hook-driven behavioral adjustments are out of scope at this stage.
  • Hook-aware indexing and enriched semantics are planned in a future release.

4.3 Positions

A Position represents a liquidity provider’s allocation within a pool.

Positions are defined by:

  • owner — current position owner
  • initiator — address that created or modified the position
  • pool — associated pool object
  • tick_lower / tick_upper — price range
  • liquidity — amount of active liquidity
  • salt — protocol-specific unique position discriminator

Positions are range-bound and active only when the current pool price falls within the specified tick range.

The API abstracts protocol-specific storage details into a consistent position model.

4.4 Swaps

A Swap represents a token exchange executed within a pool.

Swaps include:

  • Associated pool
  • Input and output tokens
  • Amounts
  • Block context
  • Transaction metadata

Swaps are indexed in deterministic block order and can be queried:

  • At the wallet level
  • At the pool level

Swap data enables:

  • Volume analytics
  • Strategy logic
  • Market activity monitoring

4.5 Fee State

Fee State represents accrued fees associated with a position.

For the current /api/v1/positions/{position_id}/fees endpoint, the response focuses on:

  • Current liquidity
  • Lifetime earned fee totals (fees_earned0_total, fees_earned1_total)

Fee growth checkpoints are tracked internally but are not exposed by this endpoint.

The API exposes position-level fee information in a structured format to simplify:

  • LP performance analysis
  • Yield calculations
  • Strategy evaluation

Fee state is derived from indexed on-chain state and reflects data up to the indexed block boundary.

4.6 Liquidity Ranges (tick_lower / tick_upper)

Liquidity in concentrated liquidity AMMs is allocated across a defined price range.

Each position specifies:

  • tick_lower — lower bound of price range
  • tick_upper — upper bound of price range

Liquidity is active only when the current pool price lies within this range. Ticks are discrete price intervals determined by the pool’s tick_spacing.

Full-range positions span the minimum and maximum tick boundaries supported by the protocol.

Understanding liquidity ranges is critical for:

  • LP profitability modeling
  • Impermanent loss estimation
  • Active liquidity calculations

4.7 Indexing Metadata

All list endpoints return indexing metadata to allow clients to reason about data freshness and completeness.

Each response includes:

  • indexed_to_block — the highest block included in the dataset
  • indexed_at — timestamp when indexing completed

This metadata allows clients to:

  • Detect indexing lag
  • Build consistency guarantees
  • Implement freshness thresholds
  • Reconcile internal state with blockchain height

LP Intelligence prioritizes transparency in indexing boundaries rather than implying real-time guarantees.

5. Endpoints

All endpoints are versioned under:

/api/v1

All responses are returned as application/json.

List endpoints:

  • Support cursor-based pagination
  • Return deterministically ordered results
  • Include indexing metadata where applicable

For full request/response schemas and parameter details, see the API Reference.

👉 Full API Reference (OpenAPI / ReDoc)

5.1 Address-Scoped Endpoints

Note: The /wallets/{address} path accepts any valid EVM address (EOA or contract).

Important: The {address} parameter is interpreted strictly as the transaction initiator (tx.from), not as a protocol-level owner field.

Returned objects may include both:

  • initiator — the transaction sender (tx.from)
  • sender — protocol-level address emitted in liquidity events

These values may differ in router, vault, multisig, or relayer-based interactions.

GET /api/v1/wallets/{address}/positions

Returns aggregated liquidity positions derived from ModifyLiquidity events initiated by the specified address.

Liquidity is calculated as the net sum of liquidity deltas across transactions where the address was the transaction initiator (tx.from).

This endpoint is filtered by transaction initiator (tx.from) and does not filter by the current position owner.

Use cases:

  • Strategy monitoring
  • Operator analytics
  • Execution tracing

GET /api/v1/wallets/{address}/swaps

Returns swap activity executed in transactions initiated by the specified address.

Provides execution-level swap history enriched with pool context.

5.2 Pools

GET /api/v1/pools

Returns indexed liquidity pools for a given chain.

Pools represent canonical market units within the supported protocol and include normalized token metadata.

Supports filtering by:

  • chain
  • token address

Note — Hooks (Uniswap v4):
The hooks field is returned as metadata. Hook execution logic is not currently modeled in computed semantics.


GET /api/v1/pools/{pool_id}/swaps

Returns indexed swap activity for a specific pool.

Enables:

  • Volume aggregation
  • Liquidity utilization analysis
  • Market activity monitoring

5.3 Positions

GET /api/v1/positions/{position_id}/fees

Returns lifetime cumulative fees earned by a liquidity position.

This endpoint is based on indexed liquidity modification checkpoints.

If swaps occurred after the last processed checkpoint, totals may temporarily lag until the next checkpoint is indexed.

5.4 Reference Data

GET /api/v1/tokens

Returns canonical token metadata for a given chain.

Supports basic search by symbol or name.


GET /api/v1/chains

Returns supported blockchain networks within LP Intelligence.

Enables programmatic discovery of:

  • supported chain IDs
  • human-readable names
  • native currency metadata
  • explorer URLs

6. Errors

LP Intelligence uses standard HTTP status codes to indicate success or failure of an API request.

Clients should always:

  • Inspect the HTTP status code
  • Parse the response body for structured error details
  • Implement retry logic where appropriate

6.1 HTTP Status Codes

The API uses the following status codes:

200 — OK

The request was successful.

400 — Bad Request

The request is malformed or contains invalid parameters.

401 — Unauthorized

Authentication credentials are missing or invalid.

403 — Forbidden

Authentication succeeded but the request is not permitted for the current API key or access tier.

404 — Not Found

The requested resource does not exist.

422 — Validation Error

The request parameters failed schema validation.

429 — Too Many Requests

Rate limit exceeded.

500 — Internal Server Error

Unexpected server-side error.

6.2 Error Response Format

Error responses are returned in JSON format.

For validation errors, the API follows a structured schema:

{
  "detail": [
    {
      "loc": ["query", "limit"],
      "msg": "ensure this value is less than or equal to 500",
      "type": "value_error.number.not_le"
    }
  ]
}

Fields:

  • loc — location of the invalid field (path, query, body)
  • msg — human-readable error message
  • type — machine-readable error classification

Clients should rely on:

  • HTTP status code for control flow
  • type for programmatic handling
  • msg for logging or debugging

6.3 Validation Errors (422)

A 422 Unprocessable Entity response is returned when request parameters fail validation.

Common causes:

  • Invalid address format
  • Invalid position_id format (must be 0x-prefixed bytes32)
  • Exceeding limit constraints
  • Invalid enum value (e.g., unsupported protocol)

Example:

{
  "detail": [
    {
      "loc": ["path", "position_id"],
      "msg": "string does not match required pattern",
      "type": "value_error.str.regex"
    }
  ]
}

Clients should treat validation errors as non-retryable unless request parameters are corrected.

6.4 Auth Errors (401/403)

401 Unauthorized

Returned when:

  • Authorization header is missing
  • API key is invalid
  • API key is revoked
{
  "error": "invalid_api_key",
  "message": "The provided API key is invalid."
}

403 Forbidden

Returned when:

  • API key is valid but lacks permission
  • Access tier does not allow requested resource
  • Chain or protocol not enabled for the key

Example:

{
  "error": "access_denied",
  "message": "Your API key does not have access to this resource."
}

Clients should not retry 401 or 403 errors without changing credentials or upgrading access.

6.5 Rate Limit Errors (429)

If a client exceeds the allowed request rate, the API returns:

429 Too Many Requests

Example:

{
  "error": "rate_limit_exceeded",
  "message": "You have exceeded your request limit. Please retry later."
}

Clients should implement exponential backoff when receiving 429 responses.

Recommended retry strategy:

  • Initial delay: 500–1000 ms
  • Exponential backoff with jitter
  • Maximum retry attempts: 3–5

Future versions may include rate limit headers such as:

  • X-RateLimit-Limit
  • X-RateLimit-Remaining
  • Retry-After

6.6 Idempotency / Retries guidance (even if you don’t support idempotency yet, retries matter)

All current endpoints are read-only (GET) and are therefore idempotent.

Safe to retry:

  • 429 (rate limit)
  • 500 (server error)
  • Network timeouts

Do not retry without modification:

  • 400 (bad request)
  • 401 (unauthorized)
  • 403 (forbidden)
  • 422 (validation error)

Recommended retry pattern:

  • Treat GET endpoints as safe to retry
  • Implement exponential backoff
  • Log request ID and error payload for diagnostics
  • Avoid aggressive retry loops under sustained 429 responses

7. Pagination & Filtering

Most list endpoints return potentially large result sets. LP Intelligence provides cursor-based pagination to keep responses fast, stable, and safe for production workloads.

Supported list endpoints use:

  • limit (page size)
  • cursor (opaque pointer to the next page)
  • next_cursor (returned by the API when more results are available)

7.1 limit

limit controls the maximum number of items returned in a single response.

  • Type: integer
  • Typical default: 50
  • Max values:
    • Most list endpoints: up to 500
    • Tokens search: up to 1000 (per your OpenAPI)

Guidance:

  • Use smaller limit values for latency-sensitive UIs.
  • Use larger values for backfills/batch jobs, but expect larger payload sizes.

Example:

GET /api/v1/pools?chain_id=1&limit=100

7.2 cursor / next_cursor

cursor (request)

cursor is an opaque string used to request the next page of results. You should treat it as a black box (do not parse or generate it).

  • Type: string (nullable)
  • Provided by: next_cursor from the previous response

next_cursor (response)

If more results exist, the response includes a next_cursor. If you reached the end, next_cursor is null.

Example flow:

  1. First page (no cursor):
GET /api/v1/wallets/{address}/positions?chain_id=1&limit=50
  1. Response includes:
{
  "items": [ /* ... */ ],
  "next_cursor": "eyJ2Ijo...opaque...",
  "limit": 50
}
  1. Next page:
GET /api/v1/wallets/{address}/positions?chain_id=1&limit=50&cursor=eyJ2Ijo...opaque...

Important rules:

  • Always pass the same query parameters when paginating (same chain_id, filters, etc.).
  • Don’t reuse a cursor across different endpoints.
  • Don’t store cursors long-term for business logic; treat them as short-lived pagination pointers.

7.3 Ordering Guarantees

List endpoints return results in a consistent, deterministic order suitable for pagination.

What you can rely on:

  • Ordering is stable within a given endpoint + parameter set.
  • cursor pagination is aligned with that ordering so you don’t miss or duplicate items while paging.

What you should not assume:

  • The exact sort keys or internal ordering logic (it may evolve without breaking pagination).
  • That “newest first” or “oldest first” is universally applied unless explicitly stated per endpoint.

Practical guidance:

  • For UIs: assume “most relevant / most recent” unless the endpoint documentation states otherwise.
  • For ingestion jobs: page until next_cursor = null.

7.4 Deterministic Responses

LP Intelligence is an indexed data API, which means responses represent a snapshot at an indexing boundary, not necessarily the latest chain head.

To support deterministic integration behavior:

  • Many responses include indexing metadata (e.g., indexed_to_block, indexed_at), allowing you to detect when the underlying dataset advanced.
  • Pagination is designed to be consistent within an indexing window.

Responses include indexed_to_block, which indicates the highest indexed block included in the dataset. Clients performing large historical backfills may use this value to ensure consistency across paginated requests.

8. Data Freshness & Indexing

8.1 Historical Indexing Model

8.2 Current Freshness Window

8.3 How to Interpret indexed_to_block

8.4 Planned Near Real-Time Ingestion

9. Versioning

10. Roadmap

11. Changelog