Skip to main content

afly

CI PyPI version Python

Idempotent AppsFlyer aggregate reports → ClickHouse, with a dbt-style CLI.

afly pulls AppsFlyer's aggregate Pull API reports and writes them into ClickHouse. It's a dbt/detectkit-style project: an afly_project.yml describes the project, profiles.yml holds credentials, and extracts/*.yml declare which reports to pull and where they land.

Why

  • Idempotent by construction, not by convention. Every run rebuilds whole ClickHouse partitions atomically (REPLACE PARTITION) instead of appending — re-running the same window, or recovering from a crash mid-run, never duplicates a row and never needs FINAL on read.
  • Quota-aware. AppsFlyer enforces a per-minute limit and a small daily budget for wider date ranges. afly's scheduler interleaves apps/report types to stay under both automatically, and a backfill can be capped with --max-calls/--max-minutes and safely resumed later.
  • The Facebook split is a config concern, not a bug. AppsFlyer only returns campaign/adset/adgroup breakdown columns for a Facebook-scoped pull — afly's scaffold ships a dedicated facebook extract alongside an unfiltered one that explicitly excludes it, so the ownership is explicit instead of accidentally double-counted.
  • A --dry-run you can actually trust. Before pulling anything, afly run --dry-run prints the exact window/chunk plan and the AppsFlyer quota totals it would spend, flagging anything that would exceed budget.

Install

pip install afly

Requires Python 3.10+.

60-second quickstart

afly init my_project && cd my_project
cp .env.example .env        # fill in AppsFlyer token + ClickHouse credentials
set -a; source .env; set +a

afly validate                       # config sanity check, no network calls
afly debug                          # probe AppsFlyer/ClickHouse connectivity
afly run --select "*" --dry-run     # print the plan, pull nothing
afly run --select "*"               # pull for real
SELECT date, media_source, sum(total_cost)
FROM appsflyer.appsflyer_geo_by_date
GROUP BY 1, 2
ORDER BY 1, 2;

See Quickstart for the full walkthrough.

The three-extract picture

afly init scaffolds a realistic split that demonstrates the ownership convention every extra media-source extract should follow:

# extracts/standard.yml — everything else
name: standard
report_type: geo_by_date_report
exclude_media_sources: [Facebook Ads, yandexdirect_int]   # owned below
table: appsflyer_geo_by_date

# extracts/facebook.yml — needs its own scoped pull for adset/adgroup columns
name: facebook
media_source: facebook
table: appsflyer_geo_by_date

# extracts/yandex.yml — reports arrive late, so re-pull a wider window
name: yandex
media_source: yandexdirect_int
lookback_days: 7
table: appsflyer_geo_by_date

All three write the same table safely, because standard explicitly excludes what the other two own. See Extracts guide and Facebook split.

AI-native onboarding

afly init-claude

Scaffolds CLAUDE.md + .claude/rules/afly/ + four skills (afly-setup-project, afly-new-extract, afly-backfill, afly-debug-run) into the project, so Claude Code (or another AI assistant) can configure extracts, size a backfill against the AppsFlyer quota, and debug a failing run with the real reference instead of guessing. Idempotent — safe to re-run after upgrading afly. See Claude Code guide.

Documentation

Requirements

  • Python 3.10+
  • ClickHouse (tested against 22.11)
  • An AppsFlyer account with Pull API (API V2) access

License

MIT License — see LICENSE for details.

Release files for afly 0.1.1

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

Source distribution (sdist)

Source distribution for afly 0.1.1
File Size Uploaded
afly-0.1.1.tar.gz 133.2 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for afly 0.1.1
File Interpreter ABI Platform
afly-0.1.1-py3-none-any.whl Python 3 none any Details

Total release size: 299.3 kB

Release files / afly-0.1.1.tar.gz

Download URL afly-0.1.1.tar.gz
Size 133.2 kB
Tags Source
SHA-256 checksum
How to use checksums
99425caa0603ca07d9e4266c7bb1c640b1e529d6873cc5bdd3b14f16093738f9
BLAKE2b-256 checksum
How to use checksums
7802528d4f4b9c01a4346960b5157fe498202b1c5a6ff5159c65551cb3191bdb
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release files / afly-0.1.1-py3-none-any.whl

Download URL afly-0.1.1-py3-none-any.whl
Size 166.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
380d1fada05b7871d56f5cb502ac047988ba9655b97a54433171d8770ee57e67
BLAKE2b-256 checksum
How to use checksums
21d10023d3b17684bd063721772deea15e595e9f4a61415c6e1aba578c5b1ea4
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.14

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Sep 25, 2026.

Transparency log

Release history Release notifications | RSS feed

This release

0.1.1 This release

2 release files

0.1.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