Skip to main content

Facebook Ads Reports Helper

A Python ETL driver for Facebook Marketing API v25 data extraction and transformation. Simplifies the process of extracting Facebook Ads data and converting it to structured data formats with comprehensive utility functions.

PyPI version Last Commit Issues License

Features

  • Facebook Marketing API v25: Latest API version support with full compatibility
  • Robust Error Handling: Comprehensive error handling with retry logic and specific exceptions
  • Multiple Report Types: Pre-configured report models for common use cases
  • Custom Reports: Create custom report configurations
  • Flexible Data Export: Built-in CSV and JSON export utilities
  • Date Range Utilities: Helper functions to break date ranges into monthly or weekly periods
  • Lightweight Architecture: No pandas dependency for faster installations and smaller footprint
  • Type Hints: Full type hint support with strict mypy compliance for better IDE experience
  • Data Processing Utilities: Helper functions for data transformation and export
  • Pixel Stats: get_pixel_stats() / get_pixel_event_hosts() read the pixel's own event counts, including events Insights never attributes to an ad
  • Token Verification: verify_token() reports validity, type, expiry, and scopes via /debug_token
  • Unicode-Safe Text Cleaning: Response cleanup preserves accents and Unicode while removing null bytes and unsafe control characters
  • Warehouse-Safe Column Names: Flattened action columns are sanitized to valid identifiers, so offsite_conversion.fb_pixel_purchase lands as offsite_conversion_fb_pixel_purchase
  • Conversion-Scope Filtering: ad_insights_report ships an action_types allow-list keeping Meta's deduplicated omni_* metrics and dropping the overlapping attribution scopes that duplicate them

Installation

pip install facebook-ads-reports

Using uv:

uv add facebook-ads-reports

Quick Start

1. Set up credentials

Option A: Configuration file

Create a secrets/fb_business_config.json file with your Facebook Ads API credentials:

{
  "app_id": "YOUR_APP_ID",
  "app_secret": "YOUR_APP_SECRET",
  "access_token": "YOUR_ACCESS_TOKEN",
  "ad_account_id": "act_1234567890",
  "base_url": "https://graph.facebook.com/v25.0"
}

access_token is required. app_id and app_secret are optional but recommended -- verify_token() uses them to authenticate its /debug_token call. base_url is ignored; the API version is pinned to v25.0 in the client.

For unattended extraction, use a Business Manager system user token rather than a Graph API Explorer token: user tokens are invalidated by password changes and expire in ~60 days, and this package has no token-refresh logic. verify_token() reports type: SYSTEM_USER and never_expires: True for a correctly issued one.

Option B: Environment variable

Set the FACEBOOK_ADS_CONFIG_JSON environment variable with your credentials as JSON:

export FACEBOOK_ADS_CONFIG_JSON='{"app_id": "YOUR_APP_ID", "app_secret": "YOUR_APP_SECRET", "access_token": "YOUR_ACCESS_TOKEN", "ad_account_id": "act_1234567890", "base_url": "https://graph.facebook.com/v25.0"}'

2. Basic usage

from datetime import date, timedelta
from facebook_ads_reports import MetaAdsReport, MetaAdsReportModel
from facebook_ads_reports.utils import load_credentials, save_report_to_csv, save_report_to_json

# Load credentials
credentials = load_credentials()
client = MetaAdsReport(credentials_dict=credentials)

# Fail fast on a bad token instead of hitting an opaque 401 mid-extraction
client.verify_token(required_scopes=["ads_read"])

# Configure report parameters
ad_account_id = "act_1234567890"
start_date = date.today() - timedelta(days=7)
end_date = date.today() - timedelta(days=1)

# Extract report data
data = client.get_report(
  ad_account_id=ad_account_id,
  report_model=MetaAdsReportModel.ad_insights_report,
  start_date=start_date,
  end_date=end_date,
  flatten=True,
  limit=200,
)

# Save to CSV using utility function
save_report_to_csv(data, "ad_insights_report.csv")

# Save to JSON using utility function
save_report_to_json(data, "ad_insights_report.json")

With flatten=True, ad_insights_report expands each action_type into its own column. The action_types allow-list in the model caps which columns are possible -- 48 on a live extract, down from 102 unfiltered -- but a column still only appears when that action occurred in the window, so absent is not zero and loaders must tolerate missing keys. See docs/REPORT_FIELDS.md.

Conversion columns use Meta's deduplicated omni_* family (omni_purchase, value_omni_purchase, roas_omni_purchase). The overlapping offsite_conversion.*, onsite_web_* and legacy bare scopes are commented out in models.py -- uncomment a block to restore them. See docs/REPORT_FIELDS.md.

Available Report Models

Model Returns Grain (one row per)
ad_accounts_report Account metadata for every account the token can reach ad account
campaigns_report Campaign setup, objective, budget campaign
adsets_report Ad set config, targeting, learning stage ad set
ad_summary_report Ad metadata, status, targeting ad
ad_dimensions_report Ad attributes with no metrics ad
ad_images_report Image assets, including the only permanent creative URL image asset
ad_insights_report Metrics and actions over time ad x day x publisher platform x platform position
ad_performance_report Backward-compatible alias of ad_insights_report same as above

Only ad_insights_report uses start_date / end_date; the other models rely on the date_preset in their own params and ignore the dates you pass. ad_accounts_report ignores ad_account_id as well, and ad_images_report declares no date_preset at all.

You can also list models dynamically with MetaAdsReportModel.list_available_reports().

Full field lists, flattening behavior, and output schemas: docs/REPORT_FIELDS.md.

Pixel Stats

Insights returns only the conversions Meta credited to an ad. To count everything a pixel received (organic, direct and paid alike) read the pixel's stats edge:

from facebook_ads_reports import MetaAdsReport

client = MetaAdsReport(credentials)

# Event totals per hour. end_date is inclusive.
rows = client.get_pixel_stats("2082629071758453", "2026-09-07", "2026-10-04", aggregation="event")
# {'pixel_id': ..., 'start_time': '2026-10-01T00:00:00-0300', 'date': '2026-10-01',
#  'hour': 0, 'aggregation': 'event', 'value': 'testride_sucesso', 'count': 3}

# Events crossed with hostname (one request per event).
rows = client.get_pixel_event_hosts("2082629071758453", "2026-09-07", "2026-10-04",
                                    events=["testride_sucesso"], sleep_seconds=15)
# {'pixel_id': ..., 'date': '2026-10-01', 'hour': 0, 'event': 'testride_sucesso',
#  'host': 'www.tripleducati.com.br', 'count': 3}

Requirements and caveats (runnable version: examples/pixel_stats.py):

  • The token's user needs access to the pixel and ads_management; ads_read alone is rejected with (#100) Permission Denied.
  • Failures are handled like get_report(): rate limits and transient errors are retried with backoff, token errors raise AuthenticationError, permission errors raise APIError.
  • get_pixel_event_hosts() costs 1 + number_of_events requests; pass events=[...] to skip discovery and sleep_seconds to space them out.
  • Dates are calendar days in the pixel owner's time zone, read from its owner ad account (pass timezone="America/Sao_Paulo" to override or when the pixel has no owner account). This is the same rule Insights follows with the ad account's time zone. The raw edge reads date-only values as UTC, so the client sends local midnight with its offset.
  • Buckets are hourly. date and hour come from the start_time string as given, so they are local to that same time zone.
  • No campaign or ad dimension, and the counts already include ad-attributed events. Do not add them to Insights conversions.

Custom Reports

Create custom model metadata templates:

from facebook_ads_reports import create_custom_report

custom_report = create_custom_report(
    report_name="my_custom_report",
    select=["ad_id", "impressions", "spend"],
    from_table="ad_insights"
)

# This helper is intended for custom ETL metadata flows and CANNOT be passed to
# get_report() -- it has no endpoint/fields/params. To define a real custom extraction,
# build a dict in the same shape as the built-in models. See docs/REPORT_FIELDS.md.

Examples

Check the examples/ directory for comprehensive usage examples:

  • basic_usage.py - Simple report extraction
  • pixel_stats.py - Pixel event counts by hour and by hostname, with a daily summary and CSV export

Requirements

  • Python 3.11-3.14
  • requests >= 2.32.4
  • python-dotenv >= 1.1.1

Development & Publishing

# install runtime + dev dependencies
uv sync --all-groups

# quality gates
uv run pytest
uv run mypy facebook_ads_reports

# local build check
uv build

tests/ currently covers the pixel stats methods; mypy remains the main quality gate.

Publishing is automated through .github/workflows/release.yml, which runs on every push to main: a Python 3.11-3.14 test matrix, then a build-and-publish job that skips PyPI if the version in pyproject.toml already exists. In practice, releasing is a version bump merged to main.

For internals and known behaviors, see docs/ARCHITECTURE.md. For the release runbook, see docs/RELEASE_PIPELINE_SKILL.md (note: it documents a release-triggered design that does not match the current workflow).

License

GPL License. See LICENSE file for details.

Support

Contributing

Contributions are welcome! Please feel free to submit a Pull Request.

Metadata

Release files for facebook-ads-reports 2.6.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for facebook-ads-reports 2.6.0
File Size Uploaded
facebook_ads_reports-2.6.0.tar.gz 91.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for facebook-ads-reports 2.6.0
File Interpreter ABI Platform
facebook_ads_reports-2.6.0-py3-none-any.whl Python 3 none any Details

Total release size: 133.2 kB

Release files / facebook_ads_reports-2.6.0.tar.gz

Download URL facebook_ads_reports-2.6.0.tar.gz
Size 91.5 kB
Tags Source
SHA-256 checksum
How to use checksums
18167ed9790816d83070c287334e5aafe7270bab6dac65bcdc478fcf27382c08
BLAKE2b-256 checksum
How to use checksums
9988b38ef6135d1ea38a86eb032bb13003001ec8d6355cbcdcad2faec445fd39
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release files / facebook_ads_reports-2.6.0-py3-none-any.whl

Download URL facebook_ads_reports-2.6.0-py3-none-any.whl
Size 41.7 kB
Tags Python 3
SHA-256 checksum
How to use checksums
657fadbfd9fb3b95f589b43cf2c88d6638dae5877adb165f6b82fbce41cf5e6f
BLAKE2b-256 checksum
How to use checksums
9e7f29156c54b0a26ee86f665d3e88fdb41f1560163f822c18268a23883d1cf8
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via uv/0.12.23 {"installer":{"name":"uv","version":"0.12.23","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"Ubuntu","version":"24.04","id":"noble","libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":true}

Release history Release notifications | RSS feed

This release

2.6.0 This release

2 release files

2.5.0

2 release files

2.4.0

2 release files

2.3.2

2 release files

2.3.1

2 release files

2.3.0

2 release files

2.2.1

2 release files

2.1.2

2 release files

2.1.1

2 release files

2.1.0

2 release files

2.0.1

2 release files

2.0.0

2 release files

1.0.1

2 release files

1.0.0

2 release files

0.9.1

2 release files

0.9.0

2 release files

0.2.0

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page