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

# Fetch Records Reporting

> Extracts records from record reporting structures or custom data tables. Records represent the underlying data used in commission calculations—CRM deals, activities, and custom metrics. Use this endpoint for sales analytics, pipeline reports, commission audit trails, CRM data extraction, and custom data table analysis.

## Overview

This endpoint extracts records from record reporting structures or custom data tables. Records represent the **underlying data** used in commission calculations — CRM deals, activities, and custom metrics.

**Use this endpoint for:**

* Sales analytics and pipeline reports
* Commission audit trails (what deals contributed to commissions)
* CRM data extraction
* Custom data table analysis

<Info>
  **Records = Details.** This endpoint gives you the source data that flows
  through Qobra's calculations, not the final commission amounts.
</Info>

***

## Pagination

This endpoint uses **ID-based pagination** for consistent performance with large datasets.

### Basic pagination pattern

```python theme={null}
url = f"https://api.qobra.co/v2/reporting/{table_id}/records"
params = {"limit": 2000}

while True:
    response = requests.get(url, headers=headers, params=params)
    result = response.json()

    # Process records
    for record in result["data"]:
        process(record)

    # Check if more pages exist
    if not result["meta"]["has_more"]:
        break

    # Use next_url for next page
    url = result["meta"]["next_url"]
```

<Tip>
  **Use `next_url` for simplicity:** The API returns a pre-constructed
  `next_url` in the response meta — just follow it instead of manually building
  the next request.
</Tip>

***

## Filtering by modification date

Use `last_modified_after` for incremental sync (only fetch new/updated records).

```python theme={null}
from datetime import datetime, timezone

def incremental_sync(table_id: str, api_key: str):
    # Load last sync time from your database
    last_sync = get_last_sync_time()  # e.g., 2024-01-14T00:00:00Z

    url = f"https://api.qobra.co/v2/reporting/{table_id}/records"
    headers = {"X-API-Key": api_key}
    params = {
        "limit": 2000,
        "last_modified_after": last_sync.isoformat()
    }

    new_records = []

    while True:
        response = requests.get(url, headers=headers, params=params)
        result = response.json()

        new_records.extend(result["data"])

        if not result["meta"]["has_more"]:
            break

        url = result["meta"]["next_url"]

    # Save current time as last sync
    save_last_sync_time(datetime.now(timezone.utc))

    return new_records

# Usage: Hourly incremental sync
new_records = incremental_sync(table_id, api_key)
print(f"Found {len(new_records)} new/updated records since last sync")
```

***

## Performance & best practices

<CardGroup cols={2}>
  <Card title="Use ID-based pagination" icon="forward">
    Always use `next_url` for large datasets.

    ```python theme={null}
    url = result["meta"]["next_url"]
    ```
  </Card>

  <Card title="Incremental sync" icon="clock">
    Use `last_modified_after` for efficiency.

    ```python theme={null}
    params = {"last_modified_after": last_sync}
    ```
  </Card>

  <Card title="Use Limit 2000" icon="gauge">
    Optimal balance between network overhead and response time.

    ```python theme={null}
    params = {"limit": 2000}
    ```
  </Card>
</CardGroup>


## OpenAPI

````yaml get /v2/reporting/{table_id}/records
openapi: 3.0.2
info:
  version: 2.0.0
  title: Qobra API v2
  description: >-
    Modern data extraction API for Qobra - Optimized for high-volume data
    extraction with ID-based pagination and flexible schema discovery.
  license:
    name: Proprietary
    url: https://www.qobra.co/terms
servers:
  - url: https://api.qobra.co
    description: Production API
security: []
paths:
  /v2/reporting/{table_id}/records:
    get:
      tags:
        - public_api
        - v2
        - reporting
      summary: Fetch Records Reporting
      description: >-
        Extracts records from record reporting structures or custom data tables.
        Records represent the underlying data used in commission
        calculations—CRM deals, activities, and custom metrics. Use this
        endpoint for sales analytics, pipeline reports, commission audit trails,
        CRM data extraction, and custom data table analysis.
      operationId: GET_/v2/reporting/(table_id)/records
      parameters:
        - name: table_id
          in: path
          required: true
          schema:
            type: string
            format: ObjectId
          description: >-
            ID of a record reporting or data table structure (from
            /v2/data-structures)
        - name: start_id
          in: query
          required: false
          schema:
            type: string
            format: ObjectId
          description: Start after this record ID (for ID-based pagination, recommended)
        - name: limit
          in: query
          required: false
          schema:
            type: integer
            minimum: 1
            maximum: 2000
            default: 2000
          description: Number of records per page (1-2000)
        - name: last_modified_after
          in: query
          required: false
          schema:
            type: string
            format: date-time
          description: >-
            ISO 8601 datetime - Only return records modified after this
            timestamp (for incremental sync)
        - name: last_modified_before
          in: query
          required: false
          schema:
            type: string
            format: date-time
          description: >-
            ISO 8601 datetime - Only return records modified before this
            timestamp
      responses:
        '200':
          description: >-
            Successfully retrieved data records. Returns an array of records
            from record reporting or data tables with pagination metadata for
            navigating through large datasets.
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/RecordsResponse'
              examples:
                records-success:
                  $ref: '#/components/examples/RecordsResponseExample'
        '400':
          description: Bad Request
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/InvalidErrorResponse'
              examples:
                invalid-error:
                  $ref: '#/components/examples/InvalidErrorResponseExample'
        '401':
          description: Unauthorized
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/UnauthorizedErrorResponse'
              examples:
                unauthorized-error:
                  $ref: '#/components/examples/UnauthorizedErrorResponseExample'
        '404':
          description: Not Found
          content:
            application/json:
              schema:
                $ref: '#/components/schemas/NotFoundErrorResponse'
              examples:
                not-found-error:
                  $ref: '#/components/examples/NotFoundErrorResponseExample'
      security:
        - api_key: []
components:
  schemas:
    RecordsResponse:
      type: object
      properties:
        data:
          type: array
          items:
            $ref: '#/components/schemas/DataRecordModel'
          description: List of records
        meta:
          $ref: '#/components/schemas/MetaModel'
      required:
        - data
        - meta
    InvalidErrorResponse:
      type: object
      properties:
        count:
          type: number
          description: Number of errors
        errors:
          type: array
          items:
            type: object
            properties:
              error:
                type: string
                description: Error type
              resource:
                type: string
                description: Resource that caused the error
              description:
                type: string
                description: Description of the error
            required:
              - error
              - resource
              - description
      required:
        - count
        - errors
    UnauthorizedErrorResponse:
      type: object
      properties:
        message:
          type: string
          description: Human-readable error message
        type:
          type: string
          enum:
            - UnauthorizedError
          description: Type of the error
      required:
        - message
        - type
    NotFoundErrorResponse:
      type: object
      properties:
        message:
          type: string
          description: Human-readable error message
        resource:
          type: string
          description: Resource that caused the error
        type:
          type: string
          enum:
            - NotFoundError
          description: Type of the error
      required:
        - message
        - resource
        - type
    DataRecordModel:
      type: object
      description: >-
        Record from record reporting or data table with dynamic fields. Standard
        Qobra fields prefixed with 'standard.', data table fields with
        'datatable.'.
      properties:
        standard.id:
          type: string
          format: ObjectId
          description: Unique record identifier
        standard.user:
          type: object
          description: User associated with this record
          properties:
            id:
              type: string
              format: ObjectId
            email:
              type: string
              format: email
        standard.date:
          type: string
          description: Date associated with this record
      required:
        - standard.id
        - standard.user
        - standard.date
      additionalProperties: true
    MetaModel:
      type: object
      properties:
        next_start_id:
          type: string
          format: ObjectId
          nullable: true
          description: ID to use for the next page (null if no more records)
        has_more:
          type: boolean
          description: Whether more records exist after this page
        next_url:
          type: string
          nullable: true
          description: Pre-built URL for the next page (null if no more records)
      required:
        - has_more
        - next_url
        - next_start_id
  examples:
    RecordsResponseExample:
      value:
        data:
          - standard.id: 75b2c3d4e5f6g7h8i9j0k2l3
            datatable.opportunity_name: Acme Corp - Annual License
            datatable.account_name: Acme Corporation
            datatable.amount:
              value: 50000
              currency: USD
            custom.total_paid:
              value: 400
              currency: USD
            datatable.close_date: '2024-01-15'
            datatable.stage: Closed Won
            standard.user:
              id: 507f191e810c19729de860ea
              email: sarah.johnson@company.com
            standard.commission_impact:
              value: 2500
              currency: USD
            standard.date: '2024-01-01'
          - standard.id: 75b2c3d4e5f6g7h8i9j0k2l4
            datatable.opportunity_name: TechStart - Professional Plan
            datatable.account_name: TechStart Inc
            datatable.amount:
              value: 25000
              currency: USD
            custom.total_paid:
              value: 1200
              currency: USD
            datatable.close_date: '2024-01-18'
            datatable.stage: Closed Won
            standard.user:
              id: 507f191e810c19729de860eb
              email: sarah.johnson@company.com
            standard.commission_impact:
              value: 1250
              currency: USD
            standard.date: '2024-01-18'
        meta:
          next_start_id: 75b2c3d4e5f6g7h8i9j999l3
          has_more: true
          next_url: >-
            https://api.qobra.co/v2/reporting/507f1f77bcf86cd799439012/records?start_id=75b2c3d4e5f6g7h8i9j999l3&limit=1000
    InvalidErrorResponseExample:
      value:
        count: 1
        errors:
          - error: ValidationError
            resource: start_id
            description: >-
              Can't parse value for param 'start_id' : Value error, Invalid
              ObjectId
    UnauthorizedErrorResponseExample:
      value:
        message: You're trying to access resource you're not authorized to.
        type: UnauthorizedError
    NotFoundErrorResponseExample:
      value:
        message: We couldn't find the requested resource
        resource: ObjectModel
        type: NotFoundError
  securitySchemes:
    api_key:
      type: apiKey
      name: X-API-Key
      in: header
      description: Your Qobra API key. Generate it from Settings > API Keys in Qobra.

````