Short-Link Portfolio Destination Drift and Expiry Monitor avatar

Short-Link Portfolio Destination Drift and Expiry Monitor

Pricing

from $20.00 / 1,000 run starteds

Go to Apify Store
Short-Link Portfolio Destination Drift and Expiry Monitor

Short-Link Portfolio Destination Drift and Expiry Monitor

Resolves a portfolio of short links, tracked links and QR-code links, then compares every final destination against a stored baseline. Reports the links that broke, expired, loop, or now point somewhere else. HTTP only, no browser, no proxy, no database.

Pricing

from $20.00 / 1,000 run starteds

Rating

0.0

(0)

Developer

kingii98

kingii98

Maintained by Community

Actor stats

0

Bookmarked

2

Total users

1

Monthly active users

10 days ago

Last modified

Categories

Share

Resolve a portfolio of short links, tracked links and QR-code links each week. Compare every final destination against the destination that the previous run stored. Report the links that broke, that expired, that loop, and that now point somewhere else.

Link rot is silent. A printed QR code, an old post and a partner link keep their traffic until the destination moves or the short link expires. Nobody sees the loss until the numbers fall. This Actor is the scheduled watch that sees it first.

The Store already holds resolvers that trace one redirect chain one time. This Actor is not a resolver. The value is the baseline comparison: it remembers what each link pointed to last week, and it tells you what moved.

  • HTTP only. No browser, no proxy, no paid API, no external database.
  • The buyer supplies the portfolio. The Actor discovers no links.
  • Private, loopback and reserved targets are refused at every hop.
  • A broken link, an expired link and an empty change report are results. The run ends SUCCEEDED. A FAILED run means a real malfunction.

What it does

For each link:

  1. Walks the redirect chain one hop at a time, up to the hop limit. Each hop keeps its own status code. httpx never follows a redirect on its own.
  2. Refuses any hop that resolves to a loopback, private or reserved address.
  3. Reads up to 64 KB of the destination page and looks for a parking page, a domain sales page or a "this link has expired" notice.
  4. Compares the final host and the final path against the baseline.
  5. Writes the new destination back into the named baseline store.

Input

FieldTypeDefaultMeaning
linksarrayfour public sample links1 to 2000 links. A plain URL string, or an object with url, an optional expected_host and an optional label.
maxHopsinteger10Redirects that one link may take before the Actor stops and marks it BROKEN. 1 to 20.
timeoutSecondsinteger15Timeout of one HTTP request. 5 to 60.
concurrencyinteger8Links resolved at the same time. 1 to 20.
detectParkedPagesbooleantrueRead the destination body and look for expiry markers.
stateStoreNamestringshort-link-destination-baselineThe named key-value store that holds the baseline.

Every field has a schema default, so a run with empty input {} succeeds.

{
"links": [
"https://example.com/promo",
{ "url": "https://example.com/partner", "expected_host": "partner.example.net", "label": "Autumn flyer QR" }
],
"stateStoreName": "autumn-campaign-links"
}

Give one portfolio one store name. Two portfolios that share a store name share a baseline, and a link that appears in both is compared against whichever run wrote last.

Output

Three record types land in one dataset. The dataset schema gives each one its own view.

url, state, finalUrl, finalHost, finalPath, finalStatus, hopCount, hops (the full chain with a status code for each hop), statusChain, loopDetected, hopLimitReached, gone, parkedSignals, expectedHost, expectedHostMet, destinationChanged, previousFinalUrl, previousSeenAt, baselineKnown, reasons, error, responseTimeMs, checkedAt.

change — the change report

One row for each link whose final host or final path moved since the last run: oldFinalUrl, oldFinalHost, oldFinalPath, oldSeenAt, newFinalUrl, newFinalHost, newFinalPath, hostChanged, pathChanged, changedAt. The same list is written to the CHANGE_REPORT record of the default key-value store, so a webhook or a downstream Actor can read it in one call.

summary — one row for each run

linksRequested, linksReported, linksResolved, linksSkipped, stateCounts, linksNeedingAttention, changesReported, baselineKnownFor, firstRun, stateStoreName, verdict.

States

The state of a link is the first of these that applies.

StateMeaning
LOOPThe chain returns to a URL that it already visited.
EXPIREDThe destination answers 410 Gone, or the page holds a parking, sales or expired-link marker.
BROKENThe link could not be resolved, the chain is longer than the hop limit, or the destination answers 4xx or 5xx.
DESTINATION_CHANGEDThe final host or the final path moved since the last run, or the final host is not the expected_host that the buyer declared.
OKThe link resolves to a live destination that did not move.

The baseline

The baseline lives in the named key-value store, under the key BASELINE. It holds one entry for each link: the final URL, the final host, the final path, the final status, the state and the time it was seen. One read starts the run and one write ends it.

  • The first run reports no change. It writes the baseline. The second run is the first that can report drift.
  • A link that timed out, that looped or that answered an error keeps the destination that the previous run stored. One bad week never erases the baseline, and never raises a false change alarm.
  • A link that leaves the portfolio keeps its entry, so a run over a subset does not lose the rest. The map is bounded at 20000 entries; the oldest go first.

Schedule it

The product pays at portfolio scale and on a schedule. Run it each week or each month over the whole portfolio, and send the change rows to the person who owns the campaign.

Pricing

This Actor is pay per event.

EventUnitPrice (USD)Charged when
run-startedrun0.02The input parses, before the first HTTP request.
link-resolvedlink that the Actor resolves0.005The Actor has sent at least one request for the link.
destination-change-reportedconfirmed destination change0.02A trusted resolution moved away from the stored baseline.

A link that the Actor refuses before it sends a request, because the target is private, reserved or malformed, is not charged. It still gets a dataset row.

A change is charged only against a stored baseline. The first run charges no destination-change-reported event, because there is nothing to compare. A link that fails to resolve charges no change event either, because its destination is not trusted. An expected_host that is not reached sets the state to DESTINATION_CHANGED, and it charges no change event, because it is not a move away from a baseline.

What one monthly run costs

A portfolio of 250 links, of which 6 moved.

EventCountPrice (USD)Cost (USD)
run-started10.020.02
link-resolved2500.0051.25
destination-change-reported60.020.12
Total1.39

How the size of the portfolio moves the bill

LinksChangesUncapped (USD)Charged (USD)
2500.150.15
25061.391.39
1000205.425.00
20004010.825.00

The default maximum total charge for one run is USD 5.00. It covers about 996 links. Raise it on the run before you monitor a portfolio that is larger than that, or split the portfolio over two schedules.

Limits

LimitValue
Links for each run2000
Redirect hops for each link20
Body read for each destination64 KB
Request timeout60 s
Parallel links20
Baseline entries20000

Development

uv sync
uv run pytest
uv run ruff check .