Skip to content
Daily API Digest

Module 02 · foundations · beginner

Lesson 02 · Daily API Digest

Goal

Build a workflow that calls a public API, processes a list of results, and produces a daily digest. You'll learn the most important idea in n8n: nodes run once per item, so you rarely need loops.

Prerequisites

  • Module 01 (webhooks, expressions, Respond to Webhook)
  • Internet access (we call hn.algolia.com, no API key needed)

Concepts

  • HTTP Request: calls any API. The response becomes item JSON.
  • Split Out: turns one item containing an array into many items, one per array element. After this, every downstream node runs per story automatically.
  • Sort / Filter / Limit: list operations on items: order, keep matching, keep the first N.
  • Aggregate: the opposite of Split Out. Collapses many items back into one item with an array.
  • Two triggers, one flow: a Schedule Trigger for production and a Webhook for on-demand testing can feed the same nodes.
  • $('Node Name'): reads data from an earlier node, e.g. $('Settings').first().json.min_points.
  • $('Node').isExecuted: tells you which trigger started this run.

Build it

Step 1: Triggers

  • Schedule Trigger Every Morning: interval Days, hour 8.
  • Webhook Digest API: method GET, path digest, respond using a Respond to Webhook node.

Step 2: Settings

Edit Fields Settings, connected from both triggers:

  • min_points (Number) = {{ Number($json.query?.min_points ?? 100) }}
  • top_n (Number) = {{ Number($json.query?.top ?? 5) }}

From the schedule there's no query, so ?? falls back to the defaults. From the webhook you can override them with ?min_points=50&top=3.

Step 3: Fetch

HTTP Request Fetch Front Page: GET https://hn.algolia.com/api/v1/search, query parameters tags=front_page, hitsPerPage=50. Run it once and inspect the output: one item with a hits array.

Step 4: Split, sort, filter, limit

  1. Split Out Split Stories: field hits. You now have ~30 items.
  2. Sort Most Points First: field points, descending.
  3. Filter Popular Only: {{ $json.points }} (Number) is greater than or equal to {{ $('Settings').first().json.min_points }}.
  4. Limit Top N: max items {{ $('Settings').first().json.top_n }}.

Step 5: Shape each story

Edit Fields Format Story:

  • title = {{ $json.title }}
  • url = {{ $json.url || 'https://news.ycombinator.com/item?id=' + $json.objectID }} (Ask HN posts have no URL)
  • points (Number) = {{ $json.points }}
  • comments (Number) = {{ $json.num_comments }}
  • line = • {{ $json.title }} ({{ $json.points }} pts)

Step 6: Aggregate and summarize

  1. Aggregate Build Digest: All Item Data, destination field stories.
  2. Edit Fields Add Summary: date = {{ $now.toFormat('yyyy-MM-dd') }}, count = {{ $json.stories.length }}, text = {{ $json.stories.map(s => s.line).join('\n') }}, stories (Array) = {{ $json.stories }}.

Step 7: Respond only when called via API

IF Called via API?: {{ $('Digest API').isExecuted }} is true.

  • true → Respond to Webhook (JSON, {{ $json }})
  • false → No Operation Deliver (Exercise 3), a placeholder for email/Slack delivery.

Publish the workflow.

Test it

./test/test.sh

Expected: 7 PASS lines (default digest ≤ 5 stories, all ≥ 100 points, sorted; custom params return exactly 3).

Common problems

  • Only one item after HTTP Request: that's expected. You need Split Out on hits.
  • Filter drops everything: points compared as a string. Use the Number operator type, or enable Convert types where required.
  • Empty 200 response: no story passed the filter, so nothing reached Respond. See Exercise 2.
  • Sort error "Couldn't find the field 'points'": an empty item reached Sort. Keep Sort before Filter.