feedhook · docs
Turn a YouTube channel into a webhook
Register a channel and a callback URL; your endpoint gets a signed JSON POST
~8 seconds after every new video. YouTube's own WebSub push underneath — no polling, no API
quota, no cron. Free for 1 feed; Pro $9/mo for 10.
Quickstart
# 1. Sign up — the API key is shown once
curl -X POST https://feedhook.walls.sh/accounts -d '{"email":"you@example.com"}'
# 2. Turn a channel into a webhook (@handle, channel URL, or UC… id)
curl -X POST https://feedhook.walls.sh/subscriptions \
-H 'authorization: Bearer fh_your_key' \
-d '{"channel":"@mkbhd","callbackUrl":"https://your.app/hook"}'
# → save the returned "secret" — it signs every delivery and is shown once
# 3. Verify your receiver right now (no need to wait for a video)
curl -X POST https://feedhook.walls.sh/subscriptions/SUB_ID/test \
-H 'authorization: Bearer fh_your_key'
The webhook you receive
POST https://your.app/hook
content-type: application/json
x-feedhook-event: video.published
x-feedhook-delivery: <uuid>
x-feedhook-signature: sha256=<hex>
{
"event": "video.published",
"subscriptionId": "…",
"videoId": "dQw4w9WgXcQ",
"channelId": "UC…",
"title": "…",
"author": "…",
"publishedAt": "2026-06-11T15:54:18+00:00",
"url": "https://www.youtube.com/watch?v=dQw4w9WgXcQ",
"receivedAt": "…"
}
Respond with any 2xx within 15 seconds. Verify the signature — HMAC-SHA256 of the raw
body, keyed with your subscription's secret:
// node
import { createHmac, timingSafeEqual } from "node:crypto";
const expected = "sha256=" + createHmac("sha256", SECRET).update(rawBody).digest("hex");
const ok = timingSafeEqual(Buffer.from(expected), Buffer.from(req.headers["x-feedhook-signature"] || ""));
# python
import hmac, hashlib
expected = "sha256=" + hmac.new(SECRET.encode(), raw_body, hashlib.sha256).hexdigest()
ok = hmac.compare_digest(expected, request.headers.get("x-feedhook-signature", ""))
# ruby (rails)
expected = "sha256=" + OpenSSL::HMAC.hexdigest("sha256", secret, request.raw_post)
ok = ActiveSupport::SecurityUtils.secure_compare(expected, request.headers["x-feedhook-signature"].to_s)
Reliability — never loses a video
We don't claim "never fails"; nobody can. We claim no silent loss, with
three mechanisms you can audit:
| If… | then… |
| your endpoint is down | 8 retries over ~9 hours, every attempt persisted to disk and visible in your delivery log — retries survive our own restarts. Down longer? Failed payloads are kept: one call redelivers them |
| we miss a push (downtime, hub hiccup) | an hourly reconciliation sweep polls each channel's feed and delivers what push missed — worst case a video arrives late, never lost |
| anything else breaks | an external watchdog probes the live system every 30 minutes and runs a full subscribe→handshake drill daily |
Live, unfaked numbers: /metrics.
Endpoints
POST /accounts | {"email"} → account + one-time API key. Free: 1 feed. |
GET /account | plan, feed limit, feeds in use |
POST /account/rotate-key | new API key (shown once); old key dies immediately |
POST /subscriptions | {"channel","callbackUrl"} → subscription + one-time signing secret |
GET /subscriptions | your subscriptions |
GET /subscriptions/:id | one subscription + recent delivery log (per-attempt HTTP results) |
POST /subscriptions/:id/test | signed test.ping through the real pipeline |
POST /subscriptions/:id/deliveries/:did/redeliver | re-send a failed delivery — payloads are kept |
DELETE /subscriptions/:id | unsubscribe, free the slot |
POST /billing/checkout | Stripe Checkout for Pro — $9/mo, 10 feeds |
POST /billing/portal | customer portal — invoices, payment method, cancel anytime |
All authenticated calls: Authorization: Bearer fh_…. Machine-readable:
openapi.json · llms.txt.
Agents (MCP)
claude mcp add feedhook -e FEEDHOOK_API_KEY=fh_your_key -- npx -y feedhook-mcp
8 tools — an agent can sign up, subscribe a channel, test the receiver, read
delivery logs, and upgrade the plan without a human. Listed on the
MCP registry.
← a wall on walls.sh