{"id":48244759,"url":"https://github.com/tinybirdco/tinybird-sdk-python","last_synced_at":"2026-04-04T20:29:32.346Z","repository":{"id":113973341,"uuid":"451470712","full_name":"tinybirdco/tinybird-sdk-python","owner":"tinybirdco","description":null,"archived":false,"fork":false,"pushed_at":"2026-03-23T16:50:29.000Z","size":250,"stargazers_count":7,"open_issues_count":2,"forks_count":1,"subscribers_count":3,"default_branch":"main","last_synced_at":"2026-03-24T14:30:10.722Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Python","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tinybirdco.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":"CONTRIBUTING.md","funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2022-01-24T13:15:36.000Z","updated_at":"2026-03-23T16:50:35.000Z","dependencies_parsed_at":null,"dependency_job_id":"1df5b261-d54a-4f97-86f3-b2c4cb88a639","html_url":"https://github.com/tinybirdco/tinybird-sdk-python","commit_stats":null,"previous_names":["tinybirdco/tinybird-sdk-python"],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/tinybirdco/tinybird-sdk-python","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tinybirdco%2Ftinybird-sdk-python","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tinybirdco%2Ftinybird-sdk-python/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tinybirdco%2Ftinybird-sdk-python/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tinybirdco%2Ftinybird-sdk-python/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tinybirdco","download_url":"https://codeload.github.com/tinybirdco/tinybird-sdk-python/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tinybirdco%2Ftinybird-sdk-python/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":31413224,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-04-04T20:09:54.854Z","status":"ssl_error","status_checked_at":"2026-04-04T20:09:44.350Z","response_time":60,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2026-04-04T20:29:31.570Z","updated_at":"2026-04-04T20:29:32.323Z","avatar_url":"https://github.com/tinybirdco.png","language":"Python","funding_links":[],"categories":[],"sub_categories":[],"readme":"# tinybird-sdk (Python)\n\n\u003e **Note:** This package is experimental. APIs may change between versions.\n\nA Python SDK for defining Tinybird resources with a TypeScript-SDK-like workflow.\nDefine your datasources, pipes, and queries in Python and sync them directly to Tinybird.\n\n## Installation\n\n```bash\npip install tinybird-sdk\n```\n\n## Requirements\n\n- Python `\u003e=3.11`\n- Server-side usage only (do not expose Tinybird credentials in browser code)\n\n## Quick Start\n\n### 1. Initialize your project\n\n```bash\ntinybird init\n```\n\nThis creates:\n- `tinybird.config.json` - Configuration file\n- `lib/datasources.py` - Define your datasources\n- `lib/pipes.py` - Define your pipes/endpoints\n- `lib/client.py` - Your Tinybird client module\n\n### 2. Configure your token\n\nCreate a `.env.local` file:\n\n```env\nTINYBIRD_TOKEN=p.your_token_here\n```\n\n### 3. Define your datasources\n\n```python\n# lib/datasources.py\nfrom tinybird_sdk import define_datasource, t, engine\n\npage_views = define_datasource(\n    \"page_views\",\n    {\n        \"description\": \"Page view tracking data\",\n        \"schema\": {\n            \"timestamp\": t.date_time(),\n            \"pathname\": t.string(),\n            \"session_id\": t.string(),\n            \"country\": t.string().low_cardinality().nullable(),\n        },\n        \"engine\": engine.merge_tree(\n            {\n                \"sorting_key\": [\"pathname\", \"timestamp\"],\n            }\n        ),\n    },\n)\n```\n\n### 4. Define your endpoints\n\n```python\n# lib/pipes.py\nfrom tinybird_sdk import define_endpoint, node, p, t\n\ntop_pages = define_endpoint(\n    \"top_pages\",\n    {\n        \"description\": \"Get the most visited pages\",\n        \"params\": {\n            \"start_date\": p.date_time(),\n            \"end_date\": p.date_time(),\n            \"limit\": p.int32().optional(10),\n        },\n        \"nodes\": [\n            node(\n                {\n                    \"name\": \"aggregated\",\n                    \"sql\": \"\"\"\n                        SELECT pathname, count() AS views\n                        FROM page_views\n                        WHERE timestamp \u003e= {{DateTime(start_date)}}\n                          AND timestamp \u003c= {{DateTime(end_date)}}\n                        GROUP BY pathname\n                        ORDER BY views DESC\n                        LIMIT {{Int32(limit, 10)}}\n                    \"\"\",\n                }\n            )\n        ],\n        \"output\": {\n            \"pathname\": t.string(),\n            \"views\": t.uint64(),\n        },\n    },\n)\n```\n\n### 5. Create your client\n\n```python\n# lib/client.py\nfrom tinybird_sdk import Tinybird\nfrom .datasources import page_views\nfrom .pipes import top_pages\n\ntinybird = Tinybird(\n    {\n        \"datasources\": {\"page_views\": page_views},\n        \"pipes\": {\"top_pages\": top_pages},\n    }\n)\n\n__all__ = [\"tinybird\", \"page_views\", \"top_pages\"]\n```\n\n### 6. Optional: use a stable local import path\n\nIn larger applications, keep a single module (for example `lib/client.py`) and import from there:\n\n```python\nfrom lib.client import tinybird\n```\n\n### 7. Start development\n\n```bash\ntinybird dev\n```\n\nThis watches your schema files and syncs changes to Tinybird.\n\n### 8. Use the client\n\n```python\nfrom lib.client import tinybird\n\n# Ingest one row\ntinybird.page_views.ingest(\n    {\n        \"timestamp\": \"2024-01-15 10:30:00\",\n        \"pathname\": \"/home\",\n        \"session_id\": \"abc123\",\n        \"country\": \"US\",\n    }\n)\n\n# Query endpoint\nresult = tinybird.top_pages.query(\n    {\n        \"start_date\": \"2024-01-01 00:00:00\",\n        \"end_date\": \"2024-01-31 23:59:59\",\n        \"limit\": 5,\n    }\n)\n```\n\n### 9. Manage datasource rows\n\n```python\nfrom lib.client import tinybird\n\n# Datasource accessors support: ingest, append, replace, delete, truncate\n\ntinybird.page_views.ingest(\n    {\n        \"timestamp\": \"2024-01-15 10:30:00\",\n        \"pathname\": \"/pricing\",\n        \"session_id\": \"session_123\",\n        \"country\": \"US\",\n    }\n)\n\ntinybird.page_views.append(\n    {\n        \"url\": \"https://example.com/page_views.csv\",\n    }\n)\n\ntinybird.page_views.replace(\n    {\n        \"url\": \"https://example.com/page_views_full_snapshot.csv\",\n    }\n)\n\ntinybird.page_views.delete(\n    {\n        \"delete_condition\": \"country = 'XX'\",\n    }\n)\n\ntinybird.page_views.delete(\n    {\n        \"delete_condition\": \"country = 'XX'\",\n        \"dry_run\": True,\n    }\n)\n\ntinybird.page_views.truncate()\n```\n\n## Public Tinybird API (Optional)\n\nIf you want a low-level API wrapper decoupled from the high-level client layer,\nuse `create_tinybird_api()` directly with `base_url` and `token`:\n\n```python\nfrom tinybird_sdk import create_tinybird_api\n\napi = create_tinybird_api(\n    {\n        \"base_url\": \"https://api.tinybird.co\",\n        \"token\": \"p.your_token\",\n    }\n)\n\n# Query endpoint pipe\ntop_pages = api.query(\n    \"top_pages\",\n    {\n        \"start_date\": \"2024-01-01\",\n        \"end_date\": \"2024-01-31\",\n        \"limit\": 5,\n    },\n)\n\n# Ingest one row\napi.ingest(\n    \"events\",\n    {\n        \"timestamp\": \"2024-01-15 10:30:00\",\n        \"event_name\": \"page_view\",\n        \"pathname\": \"/home\",\n    },\n)\n\n# Ingest retry behavior (disabled by default):\n# - 429 retries use Retry-After / X-RateLimit-Reset headers.\n# - 503 retries use SDK default exponential backoff.\napi.ingest(\n    \"events\",\n    {\n        \"timestamp\": \"2024-01-15 10:31:00\",\n        \"event_name\": \"button_click\",\n        \"pathname\": \"/pricing\",\n    },\n    {\n        \"max_retries\": 3,\n    },\n)\n\n# Import rows from URL/file\napi.append_datasource(\n    \"events\",\n    {\n        \"url\": \"https://example.com/events.csv\",\n    },\n)\n\n# Delete rows matching a SQL condition\napi.delete_datasource(\n    \"events\",\n    {\n        \"delete_condition\": \"event_name = 'test'\",\n    },\n)\n\n# Delete dry run\napi.delete_datasource(\n    \"events\",\n    {\n        \"delete_condition\": \"event_name = 'test'\",\n        \"dry_run\": True,\n    },\n)\n\n# Truncate datasource\napi.truncate_datasource(\"events\")\n\n# Execute raw SQL\nsql_result = api.sql(\"SELECT count() AS total FROM events\")\n\n# Optional per-request token override\nworkspace_response = api.request_json(\n    \"/v1/workspace\",\n    token=\"p.branch_or_jwt_token\",\n)\n```\n\nThis Tinybird API is standalone and can be used without `create_client()` or `Tinybird(...)`.\n\n## JWT Token Creation\n\nCreate short-lived JWT tokens for secure scoped access to Tinybird resources.\n\n```python\nfrom datetime import datetime, timedelta, timezone\n\nfrom tinybird_sdk import create_client\n\nclient = create_client(\n    {\n        \"base_url\": \"https://api.tinybird.co\",\n        \"token\": \"p.your_admin_token\",\n    }\n)\n\nresult = client.tokens.create_jwt(\n    {\n        \"name\": \"user_123_session\",\n        \"expires_at\": datetime.now(tz=timezone.utc) + timedelta(hours=1),\n        \"scopes\": [\n            {\n                \"type\": \"PIPES:READ\",\n                \"resource\": \"user_dashboard\",\n                \"fixed_params\": {\"user_id\": 123},\n            }\n        ],\n        \"limits\": {\"rps\": 10},\n    }\n)\n\njwt_token = result[\"token\"]\n```\n\n### Scope Types\n\n| Scope | Description |\n|-------|-------------|\n| `PIPES:READ` | Read access to a specific pipe endpoint |\n| `DATASOURCES:READ` | Read access to a datasource (with optional `filter`) |\n| `DATASOURCES:APPEND` | Append access to a datasource |\n\n### Scope Options\n\n- **`fixed_params`**: For pipes, embed parameters that cannot be overridden by the caller.\n- **`filter`**: For datasources, append a SQL WHERE clause (for example, `\"org_id = 'acme'\"`).\n\n## CLI Commands\n\nThis package installs `tinybird` as a runtime dependency.\n`tinybird generate` is handled by this SDK; other commands are delegated to the Tinybird CLI.\n\n### `tinybird init`\n\nInitialize a new Tinybird project:\n\n```bash\ntinybird init\ntinybird init --force\ntinybird init --skip-login\n```\n\n### `tinybird migrate`\n\nMigrate local Tinybird datafiles (`.datasource`, `.pipe`, `.connection`) into a Python definitions file.\n\n```bash\ntinybird migrate \"tinybird/**/*.datasource\" \"tinybird/**/*.pipe\" \"tinybird/**/*.connection\"\ntinybird migrate tinybird/legacy --out ./tinybird.migration.py\ntinybird migrate tinybird --dry-run\n```\n\n### `tinybird dev`\n\n```bash\ntinybird dev\ntinybird dev --local\ntinybird dev --branch\n```\n\n### `tinybird build`\n\n```bash\ntinybird build\ntinybird build --dry-run\ntinybird build --local\ntinybird build --branch\n```\n\n### `tinybird deploy`\n\n```bash\ntinybird deploy\ntinybird deploy --check\ntinybird deploy --allow-destructive-operations\n```\n\n### `tinybird pull`\n\n```bash\ntinybird pull\ntinybird pull --output-dir ./tinybird-datafiles\ntinybird pull --force\n```\n\n### `tinybird login`\n\n```bash\ntinybird login\n```\n\n### `tinybird branch`\n\n```bash\ntinybird branch list\ntinybird branch status\ntinybird branch delete \u003cname\u003e\n```\n\n### `tinybird info`\n\n```bash\ntinybird info\ntinybird info --json\n```\n\n## Configuration\n\nCreate a `tinybird.config.json` (or `tinybird.config.py` / `tinybird_config.py` for dynamic logic) in your project root:\n\n```json\n{\n  \"include\": [\n    \"lib/*.py\",\n    \"tinybird/**/*.datasource\",\n    \"tinybird/**/*.pipe\",\n    \"tinybird/**/*.connection\"\n  ],\n  \"token\": \"${TINYBIRD_TOKEN}\",\n  \"base_url\": \"https://api.tinybird.co\",\n  \"dev_mode\": \"branch\"\n}\n```\n\nYou can mix Python files with raw `.datasource`, `.pipe`, and `.connection` files for incremental migration.\n`include` supports glob patterns.\n\n### Config File Formats\n\nSupported config files (search order):\n\n| File | Description |\n|------|-------------|\n| `tinybird.config.py` | Python config with dynamic logic |\n| `tinybird_config.py` | Python config alias |\n| `tinybird.config.json` | JSON config (default for new projects) |\n| `tinybird.json` | Legacy JSON config |\n\nFor Python configs, export one of:\n- `config` dict\n- `CONFIG` dict\n- `default` dict\n- `get_config()` returning a dict\n\nExample:\n\n```python\n# tinybird.config.py\nconfig = {\n    \"include\": [\"lib/*.py\"],\n    \"token\": \"${TINYBIRD_TOKEN}\",\n    \"base_url\": \"https://api.tinybird.co\",\n    \"dev_mode\": \"branch\",\n}\n```\n\n### Configuration Options\n\n| Option | Type | Default | Description |\n|--------|------|---------|-------------|\n| `include` | `list[str]` | *required* | File paths or glob patterns for Python and raw datafiles |\n| `token` | `str` | *required* | API token; supports `${ENV_VAR}` interpolation. If missing, SDK falls back to `TINYBIRD_TOKEN`, then `.tinyb` |\n| `base_url` | `str` | `\"https://api.tinybird.co\"` | Tinybird API URL |\n| `dev_mode` | `\"branch\"` \\| `\"local\"` | `\"branch\"` | Development mode |\n\nIf `base_url` is omitted, SDK resolves it from `TINYBIRD_URL`, then `TINYBIRD_HOST`, then `.tinyb` (`host`), and finally defaults to `https://api.tinybird.co`.\n\n### Local Development Mode\n\nUse a local Tinybird container for development without affecting cloud workspaces:\n\n1. Start the local container:\n   ```bash\n   docker run -d -p 7181:7181 --name tinybird-local tinybirdco/tinybird-local:latest\n   ```\n\n2. Configure your project:\n   ```json\n   {\n     \"dev_mode\": \"local\"\n   }\n   ```\n\n   Or use CLI flag:\n   ```bash\n   tinybird dev --local\n   ```\n\n## Defining Resources\n\n### Connections\n\n```python\nfrom tinybird_sdk import define_gcs_connection, define_kafka_connection, define_s3_connection, secret\n\nevents_kafka = define_kafka_connection(\n    \"events_kafka\",\n    {\n        \"bootstrap_servers\": \"kafka.example.com:9092\",\n        \"security_protocol\": \"SASL_SSL\",\n        \"sasl_mechanism\": \"PLAIN\",\n        \"key\": secret(\"KAFKA_KEY\"),\n        \"secret\": secret(\"KAFKA_SECRET\"),\n    },\n)\n\nlanding_s3 = define_s3_connection(\n    \"landing_s3\",\n    {\n        \"region\": \"us-east-1\",\n        \"arn\": \"arn:aws:iam::123456789012:role/tinybird-s3-access\",\n    },\n)\n\nlanding_gcs = define_gcs_connection(\n    \"landing_gcs\",\n    {\n        \"service_account_credentials_json\": secret(\"GCS_SERVICE_ACCOUNT_CREDENTIALS_JSON\"),\n    },\n)\n```\n\n### Datasources\n\n```python\nfrom tinybird_sdk import define_datasource, engine, t\n\nevents = define_datasource(\n    \"events\",\n    {\n        \"description\": \"Event tracking data\",\n        \"schema\": {\n            \"timestamp\": t.date_time(),\n            \"event_name\": t.string().low_cardinality(),\n            \"user_id\": t.string().nullable(),\n            \"properties\": t.string(),\n        },\n        \"engine\": engine.merge_tree(\n            {\n                \"sorting_key\": [\"event_name\", \"timestamp\"],\n                \"partition_key\": \"toYYYYMM(timestamp)\",\n                \"ttl\": \"timestamp + INTERVAL 90 DAY\",\n            }\n        ),\n    },\n)\n```\n\n### Endpoints (API pipes)\n\n```python\nfrom tinybird_sdk import define_endpoint, node, p, t\n\ntop_events = define_endpoint(\n    \"top_events\",\n    {\n        \"description\": \"Get the most frequent events\",\n        \"params\": {\n            \"start_date\": p.date_time(),\n            \"end_date\": p.date_time(),\n            \"limit\": p.int32().optional(10),\n        },\n        \"nodes\": [\n            node(\n                {\n                    \"name\": \"aggregated\",\n                    \"sql\": \"\"\"\n                        SELECT event_name, count() AS event_count\n                        FROM events\n                        WHERE timestamp \u003e= {{DateTime(start_date)}}\n                          AND timestamp \u003c= {{DateTime(end_date)}}\n                        GROUP BY event_name\n                        ORDER BY event_count DESC\n                        LIMIT {{Int32(limit, 10)}}\n                    \"\"\",\n                }\n            )\n        ],\n        \"output\": {\n            \"event_name\": t.string(),\n            \"event_count\": t.uint64(),\n        },\n    },\n)\n```\n\n### Internal Pipes (not exposed as API)\n\n```python\nfrom tinybird_sdk import define_pipe, node, p\n\nfiltered_events = define_pipe(\n    \"filtered_events\",\n    {\n        \"description\": \"Filter events by date range\",\n        \"params\": {\n            \"start_date\": p.date_time(),\n            \"end_date\": p.date_time(),\n        },\n        \"nodes\": [\n            node(\n                {\n                    \"name\": \"filtered\",\n                    \"sql\": \"\"\"\n                        SELECT * FROM events\n                        WHERE timestamp \u003e= {{DateTime(start_date)}}\n                          AND timestamp \u003c= {{DateTime(end_date)}}\n                    \"\"\",\n                }\n            )\n        ],\n    },\n)\n```\n\n### Materialized Views\n\n```python\nfrom tinybird_sdk import define_datasource, define_materialized_view, engine, node, t\n\ndaily_stats = define_datasource(\n    \"daily_stats\",\n    {\n        \"schema\": {\n            \"date\": t.date(),\n            \"pathname\": t.string(),\n            \"views\": t.simple_aggregate_function(\"sum\", t.uint64()),\n            \"unique_sessions\": t.aggregate_function(\"uniq\", t.string()),\n        },\n        \"engine\": engine.aggregating_merge_tree({\"sorting_key\": [\"date\", \"pathname\"]}),\n    },\n)\n\ndaily_stats_mv = define_materialized_view(\n    \"daily_stats_mv\",\n    {\n        \"datasource\": daily_stats,\n        \"nodes\": [\n            node(\n                {\n                    \"name\": \"aggregate\",\n                    \"sql\": \"\"\"\n                        SELECT\n                          toDate(timestamp) AS date,\n                          pathname,\n                          count() AS views,\n                          uniqState(session_id) AS unique_sessions\n                        FROM page_views\n                        GROUP BY date, pathname\n                    \"\"\",\n                }\n            )\n        ],\n    },\n)\n```\n\n### Copy Pipes\n\n```python\nfrom tinybird_sdk import define_copy_pipe, node\n\n# Scheduled copy pipe\ndaily_snapshot = define_copy_pipe(\n    \"daily_snapshot\",\n    {\n        \"datasource\": events,\n        \"copy_schedule\": \"0 0 * * *\",\n        \"copy_mode\": \"append\",\n        \"nodes\": [\n            node(\n                {\n                    \"name\": \"snapshot\",\n                    \"sql\": \"\"\"\n                        SELECT today() AS snapshot_date, event_name, count() AS events\n                        FROM events\n                        WHERE toDate(timestamp) = today() - 1\n                        GROUP BY event_name\n                    \"\"\",\n                }\n            )\n        ],\n    },\n)\n\n# On-demand copy pipe\nmanual_report = define_copy_pipe(\n    \"manual_report\",\n    {\n        \"datasource\": events,\n        \"copy_schedule\": \"@on-demand\",\n        \"copy_mode\": \"replace\",\n        \"nodes\": [\n            node(\n                {\n                    \"name\": \"report\",\n                    \"sql\": \"SELECT * FROM events WHERE timestamp \u003e= now() - interval 7 day\",\n                }\n            )\n        ],\n    },\n)\n```\n\n### Sink Pipes\n\nUse sink pipes to publish query results to external systems.\nThe SDK supports Kafka and S3 sinks.\n\n```python\nfrom tinybird_sdk import define_sink_pipe, node\n\n# Kafka sink\nkafka_events_sink = define_sink_pipe(\n    \"kafka_events_sink\",\n    {\n        \"sink\": {\n            \"connection\": events_kafka,\n            \"topic\": \"events_export\",\n            \"schedule\": \"@on-demand\",\n        },\n        \"nodes\": [\n            node(\n                {\n                    \"name\": \"publish\",\n                    \"sql\": \"SELECT timestamp, payload FROM kafka_events\",\n                }\n            )\n        ],\n    },\n)\n\n# S3 sink\ns3_events_sink = define_sink_pipe(\n    \"s3_events_sink\",\n    {\n        \"sink\": {\n            \"connection\": landing_s3,\n            \"bucket_uri\": \"s3://my-bucket/exports/\",\n            \"file_template\": \"events_{date}\",\n            \"format\": \"csv\",\n            \"schedule\": \"@once\",\n            \"strategy\": \"create_new\",\n            \"compression\": \"gzip\",\n        },\n        \"nodes\": [\n            node(\n                {\n                    \"name\": \"export\",\n                    \"sql\": \"SELECT timestamp, session_id FROM s3_landing\",\n                }\n            )\n        ],\n    },\n)\n```\n\n### Static Tokens\n\n```python\nfrom tinybird_sdk import define_datasource, define_endpoint, define_token, node, t\n\napp_token = define_token(\"app_read\")\ningest_token = define_token(\"ingest_token\")\n\nevents = define_datasource(\n    \"events\",\n    {\n        \"schema\": {\n            \"timestamp\": t.date_time(),\n            \"event_name\": t.string(),\n        },\n        \"tokens\": [\n            {\"token\": app_token, \"scope\": \"READ\"},\n            {\"token\": ingest_token, \"scope\": \"APPEND\"},\n        ],\n    },\n)\n\ntop_events = define_endpoint(\n    \"top_events\",\n    {\n        \"nodes\": [node({\"name\": \"endpoint\", \"sql\": \"SELECT * FROM events LIMIT 10\"})],\n        \"output\": {\"timestamp\": t.date_time(), \"event_name\": t.string()},\n        \"tokens\": [{\"token\": app_token, \"scope\": \"READ\"}],\n    },\n)\n```\n\n## Type Validators\n\nUse `t.*` to define column types:\n\n```python\nfrom tinybird_sdk import t\n\nschema = {\n    # Strings\n    \"name\": t.string(),\n    \"id\": t.uuid(),\n    \"code\": t.fixed_string(3),\n\n    # Numbers\n    \"count\": t.int32(),\n    \"amount\": t.float64(),\n    \"big_number\": t.uint64(),\n    \"price\": t.decimal(10, 2),\n\n    # Date/Time\n    \"created_at\": t.date_time(),\n    \"updated_at\": t.date_time64(3),\n    \"birth_date\": t.date(),\n\n    # Boolean\n    \"is_active\": t.bool(),\n\n    # Complex types\n    \"tags\": t.array(t.string()),\n    \"metadata\": t.map(t.string(), t.string()),\n\n    # Aggregate functions\n    \"total\": t.simple_aggregate_function(\"sum\", t.uint64()),\n    \"unique_users\": t.aggregate_function(\"uniq\", t.string()),\n\n    # Modifiers\n    \"optional_field\": t.string().nullable(),\n    \"category\": t.string().low_cardinality(),\n    \"status\": t.string().default(\"pending\"),\n}\n```\n\n## Parameter Validators\n\nUse `p.*` to define query parameters:\n\n```python\nfrom tinybird_sdk import p\n\nparams = {\n    \"start_date\": p.date_time(),\n    \"user_id\": p.string(),\n\n    \"limit\": p.int32().optional(10),\n    \"offset\": p.int32().optional(0),\n\n    \"status\": p.string().optional(\"active\").describe(\"Filter by status\"),\n}\n```\n\n## Engine Configurations\n\n```python\nfrom tinybird_sdk import engine\n\nengine.merge_tree(\n    {\n        \"sorting_key\": [\"user_id\", \"timestamp\"],\n        \"partition_key\": \"toYYYYMM(timestamp)\",\n        \"ttl\": \"timestamp + INTERVAL 90 DAY\",\n    }\n)\n\nengine.replacing_merge_tree(\n    {\n        \"sorting_key\": [\"id\"],\n        \"ver\": \"updated_at\",\n    }\n)\n\nengine.summing_merge_tree(\n    {\n        \"sorting_key\": [\"date\", \"category\"],\n        \"columns\": [\"count\", \"total\"],\n    }\n)\n\nengine.aggregating_merge_tree(\n    {\n        \"sorting_key\": [\"date\"],\n    }\n)\n```\n\n## Python App Integration\n\nFor Python web apps (FastAPI, Django, Flask), keep Tinybird definitions and client in a dedicated module and import that module from your app services.\n\nThe CLI automatically loads `.env.local` and `.env` files in project root when resolving configuration.\n\n## Schema Inference Helpers\n\nThe `tinybird_sdk.infer` module can inspect datasource and pipe definitions:\n\n```python\nfrom tinybird_sdk.infer import infer_output_schema, infer_params_schema, infer_row_schema\n\nrow_schema = infer_row_schema(page_views)\nparams_schema = infer_params_schema(top_pages)\noutput_schema = infer_output_schema(top_pages)\n```\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftinybirdco%2Ftinybird-sdk-python","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftinybirdco%2Ftinybird-sdk-python","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftinybirdco%2Ftinybird-sdk-python/lists"}