Keeping a Video Catalogue in Sync with an API: New, Changed and Removed Videos Without Re-downloading Everything
Importing a few thousand videos from an API is the easy part. The work starts the next day: new videos appear, titles and descriptions get improved, performers get matched, and some videos are removed at the source. A site that doesn't follow those changes slowly fills with stale text and dead players. Re-importing everything every night works for a hundred videos and falls apart at a hundred thousand.
This post shows the sync loop we recommend for sites and apps built on the Pornalizer Porn API: one initial import, then small incremental updates. The same pattern works with any API that offers "changed since" and "removed since" queries.
What you store
For each video keep at least:
- The API's video id. Everything else is keyed on it.
- The fields you display (title, description, duration, thumbnail, tags, performers) and the embed URL.
- Your own state: published or not, your local post id, and when you last synced it.
And one value for the whole sync: the time of the last successful run. Save it only after a run finishes, so a crash means the next run repeats a little work instead of skipping some.
Step 1: the first import
Pull what you want with filters, page by page:
GET /api/v1/search?orientation=gay&sort=newest&per_page=100&page=1
GET /api/v1/search?orientation=gay&sort=newest&per_page=100&page=2
...
Search returns only publish-ready videos by default (clean title, real duration, a written description, tags). Don't import the whole catalogue on day one; a focused niche with a few thousand videos makes a better site than 300,000 thin pages. Our post on choosing a niche explains why.
Step 2: hourly, the changes
Every hour, ask only for videos that changed since your last run, oldest change first:
GET /api/v1/search?orientation=gay&updated_since=2026-11-09T08:00:00Z&sort=updated&per_page=100
For each result: if you have the id, update its fields; if you don't, it's a new video, so insert it. "Changed" includes better titles and descriptions, newly matched performers and new tags, so your pages improve without you doing anything.
To pick up new videos only, without updates, use published_since instead.
Step 3: daily, the removals
GET /api/v1/videos/removed?since=2026-11-08T00:00:00Z&per_page=100
This lists ids removed since that time, with the removal time. Unpublish each one you have (or answer 410 Gone for its page) and drop it from your sitemap. In one recent 24-hour period this endpoint listed about two thousand removals across the whole catalogue, mostly duplicates replaced by a better copy and files that died at the source; only the ones you imported matter to you.
Many removed videos never reach you as broken players at all: the embed URL is permanent and switches to a backup copy when the main file dies. Removals are for the cases where no backup exists.
Step 4: spot checks
- Batch refresh:
GET /api/v1/videos?ids=1,2,3,...takes up to 100 ids and returns current data, plus amissinglist of ids that no longer exist. Use it to re-check your most visited videos weekly. - One video:
GET /api/v1/videos/{id}/statusanswers{"active": true, "complete": true, "removed_at": null}. Use it when a visitor reports a problem.
The whole loop in Python
import datetime as dt, json, pathlib, requests
API = "https://pornalizer.app/api/v1"
STATE = pathlib.Path("sync_state.json")
state = json.loads(STATE.read_text()) if STATE.exists() else {"last": "2026-11-01T00:00:00Z"}
started = dt.datetime.now(dt.timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")
page = 1
while True:
r = requests.get(f"{API}/search", params={"orientation": "gay", "updated_since": state["last"],
"sort": "updated", "per_page": 100, "page": page}).json()
for v in r["results"]:
upsert_video(v) # your insert-or-update
if not r.get("next_page"):
break
page = r["next_page"]
page = 1
while True:
r = requests.get(f"{API}/videos/removed", params={"since": state["last"], "per_page": 100, "page": page}).json()
for item in r["results"]:
unpublish_if_present(item["id"]) # your unpublish / 410
if not r.get("next_page"):
break
page = r["next_page"]
state["last"] = started # only after both loops finished
STATE.write_text(json.dumps(state))
Run it hourly from cron. Note that state["last"] is set to the time the run started, so changes made during the run are picked up next time.
Caching and politeness
There's no request quota, but responses are cached server-side for a few minutes per query. Polling the same query every ten seconds returns the same answer; hourly for changes and daily for removals is plenty. Send an X-Site-URL header from server-side code so your usage is reported per site.
Using WordPress? The plugin runs this loop for you; see the WordPress plugin and the integrations page. Why freshness matters for rankings is covered in how fresh content affects SEO.
Frequently asked questions
How often should I sync with a video API?
Hourly for new and changed videos and daily for removals is enough for most sites. Faster polling returns cached answers.
How do I find out which videos were removed?
Call /videos/removed with the time of your last sync. It lists the ids removed since then; unpublish the ones you have and drop them from your sitemap.
Do I need to re-download the whole catalogue to get updated titles?
No. Request videos with updated_since set to your last sync time and sort=updated; only videos that changed come back.
Want a WordPress tube site that fills itself with tagged, categorized video — free?
Get your API key →