New to Kestra?
Use blueprints to kickstart your first workflows.
Daily Kestra ETL that fetches a REST API, converts JSON to CSV with jq, bulk loads Postgres via CopyIn, cleans up with a finally block, and alerts Slack.
Ship a complete, production shaped API ingestion pipeline in one YAML file. Every day this Kestra blueprint calls a REST endpoint, flattens the nested JSON response into CSV with jq, bulk loads it into Postgres using the native COPY protocol, verifies the loaded rows with a query, and posts the outcome to Slack. A finally block resets the staging table no matter how the run ends, and a flow level error handler ships the stack trace to Slack when something breaks.
daily trigger (io.kestra.plugin.core.trigger.Schedule) runs the flow every day at midnight in the Europe/Paris timezone.call_api task (io.kestra.plugin.core.http.Request) issues a GET to the uri input, which defaults to the Irish parliament members API so the flow works out of the box.csv task (io.kestra.plugin.scripts.shell.Commands) receives the response body as members.json via inputFiles, installs jq, extracts one CSV line per member, and exposes members.csv as an output file.load_to_pg task (io.kestra.plugin.jdbc.postgresql.CopyIn) streams the CSV into the house_members table using the Postgres COPY protocol, which is dramatically faster than row inserts. Connection details come from pluginDefaults, declared once for every Postgres task.query task (io.kestra.plugin.jdbc.postgresql.Query) reads the table back with fetchType: STORE to confirm and expose the loaded data, then slack (io.kestra.plugin.slack.notifications.SlackIncomingWebhook) posts a completion message.finally block runs clean_data regardless of success or failure, and the errors block sends {{ errorLogs() }} to Slack when the run fails.finally, keeping the staging table idempotent between runs.pluginDefaults, defined once and reused by every task.The same logic as a cron driven Python script scatters scheduling, secrets, retries, cleanup, and alerting across code and infrastructure. Kestra makes each concern a declarative block: the schedule is a trigger, credentials are secrets injected through plugin defaults, cleanup is a finally block that always runs, and failure alerting is an errors branch with the real stack trace. Every run is logged, replayable, and backfillable from the UI.
house_members table matching the two CSV columns (or your adapted schema).clean_data empties the table after each run to keep the demo idempotent, remove or change it to keep the loaded data.JDBC_CONNECTION_STRING, PG_USERNAME, PG_PASSWORD: Postgres connection shared by all JDBC tasks through pluginDefaults.SLACK_WEBHOOK: incoming webhook URL for the success and failure notifications.house_members staging table in Postgres.uri input.query task output and the message in your Slack channel.jq expression to your payload.finally delete with an upsert or a partition swap to retain history.call_api in io.kestra.plugin.core.flow.ForEach over page numbers.io.kestra.plugin.dbt.cli.Run after the load to transform the staged rows into models.retry at the flow level to absorb flaky API responses automatically.query task outputs.