Webhooks

The Serply REST API does not currently support outbound webhooks. There is no endpoint for registering a callback URL, and the API does not push events to your application.

Serply's endpoints are synchronous: you make a request, and the results come back in that same response. There is no job queue to be notified about and no search.completed event, because the search has already completed by the time the call returns.

If you need to be notified when something on the web changes, you build that loop on your side. The pattern is below.

Polling instead

Run your query on a schedule, compare against what you saw last time, and act only on the difference:

import json
import time
from pathlib import Path

import requests

HEADERS = {
    'X-Api-Key': 'YOUR_API_KEY',
    'X-Proxy-Location': 'US',  # pin the region - see the warning below
}
STATE = Path('seen.json')


def search(query, num=10):
    r = requests.get(
        f'https://api.serply.io/v1/search/q={query}&num={num}',
        headers=HEADERS,
        timeout=15,
    )
    r.raise_for_status()
    return r.json().get('results', [])


def check(query):
    seen = set(json.loads(STATE.read_text())) if STATE.exists() else set()

    new = [item for item in search(query) if item['link'] not in seen]

    if new:
        notify(new)  # Slack, email, a database write - whatever you need

    STATE.write_text(json.dumps(sorted(seen | {i['link'] for i in new})))
    return new


while True:
    check('your+query+here')
    time.sleep(900)  # every 15 minutes

Storing the state is the part that matters. Without it you re-alert on the same results every cycle, and the notifications stop being read.

Always pin X-Proxy-Location when monitoring

This is the detail that breaks most polling loops, and it is not obvious.

If you omit X-Proxy-Location, your request is served from whichever proxy is available, and the response comes back with device_region set to an empty string. Most consecutive calls then return the same results - until one is served from a different country, at which point you get an entirely different SERP.

In testing, six identical queries three seconds apart returned the same nine results five times, and on the sixth returned a German result set sharing zero links with the previous five. A monitor built on that will announce that 100% of its results are new, then revert on the next cycle.

Pinning the header removes the problem completely. The same six-run test with X-Proxy-Location: US returned identical results every time:

HEADERS = {
    'X-Api-Key': 'YOUR_API_KEY',
    'X-Proxy-Location': 'US',
}

Any change in results is then a real change in the SERP, which is the only thing worth alerting on. See Google Search for the full list of accepted regions.

Why polling is cheaper than it sounds

Credits are consumed per successful, uncached request. Cached responses cost nothing, and most endpoints cache for several minutes - Reddit endpoints cache for 10 minutes, and the Reddit comments response tells you directly with a cached field.

The practical effect is that polling faster than the cache window does not cost more. A monitor that checks every minute pays for roughly the same number of credits as one that checks every ten, because the intervening calls are served from cache.

That also means there is little value in polling very aggressively: you will receive the cached response until the window expires. Match your interval to how quickly the underlying data actually changes - for most search and news monitoring, 15 minutes to an hour is plenty.

Handling failures in a long-running loop

A polling loop runs unattended, so it needs to survive the transient errors a one-off script can ignore:

  • 502 means the upstream fetch or parse failed. It is transient - retry before treating it as an outage.
  • A 200 response with an empty results array is usually the same transient failure rather than a genuine "no results". Retry once or twice before recording that state, or your loop will alert on results "disappearing" and then "reappearing" on the next cycle.
  • 429 means you hit the rate limit. Back off and retry.

See the Errors guide for the full error format.

If you need push delivery

Some use cases genuinely need push rather than poll - high query volumes, or alerting where a 15-minute delay is too long. If that describes your setup, contact support and describe the workload; requirements like this help prioritize what gets built.