How to Implement Callback Functions for Processing Command Output in OpenBB

To process command output in OpenBB, define an async callback function that receives the raw aiohttp response object and pass it to the amake_request helper when building provider models.

The OpenBB Platform uses an asynchronous request architecture that separates data fetching from data transformation. By implementing callback functions for processing command output in OpenBB, you can intercept raw HTTP responses, apply custom parsing logic, and return structured data that integrates seamlessly with the CLI's display and registry systems.

Understanding the Callback Architecture in OpenBB

OpenBB processes data-fetching commands through the amake_request helper located in openbb_core/provider/utils/helpers.py. This utility accepts a response_callback parameter—an async function that transforms the raw response into the shape expected by the command's Pydantic model.

The Async Request Helper (amake_request)

The amake_request function serves as the bridge between HTTP transport and data processing. When a command executes, the helper opens an aiohttp session, fetches the response, then awaits your callback with the signature async def callback(response: ClientResponse, extra: any). Because the callback runs inside the request coroutine, you can perform additional async I/O such as secondary API calls or file extraction without blocking the CLI.

Where Callbacks Live in the Codebase

Provider-specific callbacks are typically organized in utility modules:

The CLI controllers in cli/openbb_cli/controllers/base_controller.py and cli/openbb_cli/controllers/base_platform_controller.py handle the --register_obbject flag, which determines whether processed output gets stored in the OBBject registry.

Implementing a Custom Callback Function

To implement callback functions for processing command output in OpenBB, create an async function that accepts the ClientResponse object and returns a Python object (dict, list, or DataFrame).

Simple CSV Parsing Callback

For providers returning CSV data, implement a callback that streams and parses the text:


# File: openbb_platform/providers/example/utils/helpers.py

import csv
from io import StringIO
from aiohttp import ClientResponse
from typing import List, Dict

async def csv_callback(response: ClientResponse, _: any) -> List[Dict]:
    """Read a CSV response and return a list of dictionaries."""
    text = await response.text()
    reader = csv.DictReader(StringIO(text))
    return [row for row in reader]

Use this callback when constructing your provider model:


# File: openbb_platform/providers/example/models/my_data.py

from ...utils.helpers import csv_callback
from openbb_core.provider.utils.helpers import amake_request

async def fetch_data(symbol: str):
    url = f"https://example.com/data/{symbol}.csv"
    raw = await amake_request(url, response_callback=csv_callback)
    return raw

Enriched Data Callback with OBBject Registration

To add metadata before the CLI registers the result, create an enrichment callback:


# File: cli/openbb_cli/controllers/custom_controller.py

from datetime import datetime
from aiohttp import ClientResponse

async def enrich_callback(response: ClientResponse, _: any):
    """Add timestamp and source metadata to the response."""
    data = await response.json()
    data["fetched_at"] = datetime.utcnow().isoformat()
    data["source"] = "custom_api"
    return data

When users pass the --register_obbject flag, BasePlatformController (lines 165-168) automatically stores the enriched object in the session registry using the logic defined in base_controller.py (lines 734-735).

Handling Paginated Responses

For APIs with pagination, implement recursive callbacks that follow next_page URLs:


# File: openbb_platform/providers/intrinio/openbb_intrinio/utils/helpers.py

from aiohttp import ClientResponse
from typing import List, Dict

async def pagination_callback(response: ClientResponse, _: any) -> List[Dict]:
    """Concatenate paginated Intrinio responses into a single list."""
    data = await response.json()
    results = data.get("data", [])
    
    while data.get("next_page"):
        next_url = data["next_page"]
        # Fetch next page using the same callback pattern

        next_data = await amake_request(next_url, response_callback=pagination_callback)
        results.extend(next_data.get("data", []))
        data = next_data
    
    return results

Integrating Callbacks into Provider Models

The command flow follows a strict injection pattern:

  1. User executes CLI command (e.g., openbb stocks.load AAPL)
  2. Controller parses arguments via BaseController.parse_known_args_and_warn
  3. Provider model builds URL and calls amake_request(url, response_callback=my_callback)
  4. Callback transforms raw response into the expected data structure
  5. Model validates data through Pydantic and returns to CLI

You do not need to modify command-level logic—the callback is injected at the request construction point in the provider model file (e.g., openbb_platform/providers/sec/openbb_sec/models/sec_filing.py).

Registering Processed Output with the OBBject Registry

OpenBB's CLI maintains an OBBject registry for persisting command results across the session. To enable this for your custom callback:

  1. Ensure your callback returns a serializable object (dict, list, or Pydantic model)
  2. Users must append --register_obbject when running the command
  3. BasePlatformController detects the flag and stores the result via session.obbject_registry.add()

Access registered objects later using the results command with the appropriate key.

Summary

  • Define async callbacks with the signature async def callback(response: ClientResponse, extra: any) to intercept HTTP responses before they reach the CLI.
  • Pass callbacks to amake_request in your provider model to transform raw data into structured formats without modifying core CLI code.
  • Leverage existing infrastructure by using the --register_obbject flag (handled in base_controller.py and base_platform_controller.py) to persist processed output in the OBBject registry.
  • Handle complex scenarios like pagination or file extraction within the callback since it executes inside the async request coroutine.

Frequently Asked Questions

What is the exact function signature for an OpenBB response callback?

An OpenBB response callback must be an async function accepting two parameters: response (an aiohttp.ClientResponse object) and extra (typically None or a session context). The function should return a Python object such as a dictionary, list, or Pydantic model. This signature is required by the amake_request helper in openbb_core/provider/utils/helpers.py.

Where should I place custom callback functions in the OpenBB codebase?

Place custom callbacks in the utils/helpers.py file within your specific provider directory (e.g., openbb_platform/providers/your_provider/openbb_your_provider/utils/helpers.py). For CLI-specific post-processing, you can define callbacks directly in the controller file at cli/openbb_cli/controllers/custom_controller.py or import them from utility modules.

How does the --register_obbject flag interact with my callback output?

When users include --register_obbject in their command, BaseController parses the flag (as implemented in cli/openbb_cli/controllers/base_controller.py lines 734-735) and BasePlatformController stores the returned object in the session registry (lines 165-168 in base_platform_controller.py). Your callback must return a serializable object for successful registration.

Can I perform additional async operations inside a callback?

Yes. Because amake_request awaits your callback within the active aiohttp session, you can safely perform additional async I/O operations such as secondary API calls, database queries, or file system operations. This is particularly useful for pagination patterns like those implemented in the Intrinio provider.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →