Polling vs Webhooks
We-Link offers two methods to retrieve results from async operations. Here's how they compare and when to use each.
Comparison
| Aspect | Webhooks ✅ | Polling ⚠️ |
|---|---|---|
| Latency | Instant (pushed on completion) | Depends on poll interval |
| Server Load | Minimal (event-driven) | High (repeated requests) |
| Reliability | At-least-once delivery | You control retry logic |
| Complexity | Requires public endpoint | Simpler to implement |
| Real-time | Yes | Near-real-time at best |
When to Use Webhooks (Recommended)
Choose webhooks when:
- You need real-time updates
- You're running in production with a public server
- You manage multiple accounts (polling doesn't scale)
- You want to minimize API calls and bandwidth
// Webhook: data comes to you
app.post("/webhook", (req, res) => {
res.status(200).send("OK");
processResult(req.body);
});
When to Use Polling
Choose polling when:
- You're prototyping or testing locally
- You can't expose a public URL
- You need to check a specific request's status
- As a fallback if a webhook was missed
// Polling: you go get the data
async function pollForResult(requestId, maxAttempts = 10) {
for (let i = 0; i < maxAttempts; i++) {
const response = await fetch("https://api.we-link.ai/api/v1/get_response", {
method: "POST",
headers: {
"Content-Type": "application/json",
"x-api-key": API_KEY,
"x-api-secret": API_SECRET,
},
body: JSON.stringify({ request_id: requestId }),
});
const data = await response.json();
if (data.status === "SUCCESS" || data.status === "FAILED") {
return data; // Task complete
}
// Wait before next poll (randomize to avoid patterns)
await sleep(randomBetween(15000, 45000));
}
throw new Error("Polling timeout");
}
Hybrid Approach (Best Practice)
Use webhooks as the primary delivery method with polling as a fallback:
// Save requestId with timestamp
await db.savePending(requestId, Date.now());
// Cron job: check for stale requests (no webhook after 5 mins)
async function reconcileStale() {
const stale = await db.getPendingOlderThan(5 * 60 * 1000);
for (const { requestId } of stale) {
const result = await pollForResult(requestId, 1);
if (result) {
await processResult(result);
await db.markComplete(requestId);
}
}
}
tip
The hybrid approach gives you the best of both worlds: real-time delivery via webhooks with guaranteed delivery via polling fallback.