Homebrew Formula Install Rankings & Change Monitor
Pricing
from $0.60 / 1,000 formula results
Homebrew Formula Install Rankings & Change Monitor
Monitor Homebrew formula install-on-request rankings. Track rank, install share and new/exited formulae via the official Formulae JSON API.
Pricing
from $0.60 / 1,000 formula results
Rating
0.0
(0)
Developer
Vadim Bezrukov
Maintained by CommunityActor stats
0
Bookmarked
2
Total users
1
Monthly active users
3 days ago
Last modified
Categories
Share
See which Homebrew formulae people are installing on purpose, whether rank or install share moved since your last successful check, and whether a watched CLI tool entered or left the window. The Actor reads the official Formulae JSON API. It does not scrape formulae.brew.sh HTML, download the 31 MiB formula catalog, or collect cask analytics.
Paste the prefill below, run once, and you get the current top-20
install-on-request formulae plus one query_status row. Next week, switch the
same metric and period to monitor + changesOnly under a stable
stateNamespace.
This is not a claim that Homebrew Store search demand is measured, and it
is not a quality comparison with other Store scrapers. Homebrew does
not provide a native install-rank history; each row is an observation at
scraped_at for that analytics window.
Choose your workflow
| Goal | Settings | What you get |
|---|---|---|
| Export today's top-N on-request ranking | snapshot + all | One formula_observation per formula, all BASELINE |
| Re-check the same window next week | monitor + changesOnly | NEW, RANK_UP, RANK_DOWN, EXITED, REAPPEARED |
| Watch named formulae | watchlist | Observations only for those names; rank comes from the full window |
These are the three jobs the Actor is built for. Saved Console examples use the same inputs; they stay unpublished until you publish each Task separately.
- Export top formulae for install-on-request, default 30-day window. First
run writes
BASELINErows and onequery_status. - Monitor rank changes on the same query later. Keep
stateNamespacestable. Later runs returnNEW,RANK_UP,RANK_DOWN,EXITEDorREAPPEARED. Daily install-count drift is not a change. - Watch named formulae such as
gh,wgetandripgrep. A name missing from a populated analytics window is delivered as an unrankedSUCCESSrow (ranknull), notNOT_FOUND.
BASELINE means "this is the first successful ranking we stored for this
query". It does not mean the formula was created today.
Quick start
{"mode": "snapshot","metric": "installOnRequest","period": "30d","topN": 20}
On the 2026-09-11 probe, rank 1 for install-on-request 30d was gh. Then
schedule the same window:
{"mode": "monitor","outputMode": "changesOnly","stateNamespace": "onrequest-30d","metric": "installOnRequest","period": "30d","topN": 20}
Keep stateNamespace stable for a given watch. Snapshot mode does not read or
write history. snapshot + changesOnly is rejected before any Homebrew
request. Changing metric, period, or (for top-N queries) topN starts a new
query_key and a new baseline. A named watchlist is keyed without topN, so
changing topN does not reset that watch.
A watchlist accepts gh or https://formulae.brew.sh/formula/gh. Other hosts
are INVALID_INPUT without a fetch. Rank is the analytics number, even when
it is greater than topN. A watchlist name missing from a populated JSON window
is delivered as an unranked SUCCESS observation (rank null), a candidate for
EXITED only after it was previously ranked, not NOT_FOUND. The query stays
SUCCESS when analytics returned a populated window; NO_RESULTS is only a
verified items: []. Optional per-formula metadata HTTP 404 is item-level
NOT_FOUND with ranking fields still filled from analytics.
Formulae JSON API vs this Actor API
- Formulae JSON API:
GET https://formulae.brew.sh/api/analytics/install-on-request/{period}.jsonand, if you turn metadata on,GET https://formulae.brew.sh/api/formula/{name}.json. Public JSON, no token. - Actor API: Apify input, Dataset rows (
formula_observation,query_status,run_summary), and optional last-good state in this Actor's key-value store.
Default metric is install-on-request, not install-all. The install metric
counts dependency installs; on 2026-09-11 its rank 1 was ca-certificates.
The Actor never requests /api/formula.json. Analytics ids may be tap-qualified
(user/tap/formula); those rows keep that identity and do not collapse onto a
core formula of the same short name.
What changed, and what did not
Monitor diffs last successful state against current successful state.
- First successful monitor, including
changesOnly, emitsBASELINE. - A new id after that is
NEW. Rank moves areRANK_UP/RANK_DOWN. installsandpercentare not compared. Homebrew rebuilds each rolling window daily, so those numbers move on almost every formula between any two runs (on 2026-09-12 all of the top 5 had new counts and identical ranks). They stay on every row as observation data; a row whose rank did not move isUNCHANGEDand, inchangesOnly, not delivered. UseoutputMode=allif you want the count series itself.UPDATEDremains in the schema for rows written before 2026-09-13 and is no longer emitted.- An id missing from two different sequential successful complete
windows becomes
EXITED. The first miss ispending_exitsonly. A transport error,OUTPUT_LIMIT, or mass-removal guard does not advance that counter. EXITEDmeans "not in this query's successful top-N / watchlist response". It does not mean the formula was deleted from Homebrew core.- If a successful top-N window keeps under 50% of the previous ids at the
same
topN, the query isPARTIAL(mass-removal guard). Named watchlists skip that guard: every missing id is a pending/EXITEDcandidate. Pending exits are not applied on mass-removal. - A
PARTIALparse window (some selected rows unparseable) still classifies the parsed rows against the last good state, but counts no exits and does not store the snapshot. The nextSUCCESSwindow repeats those events with the sameevent_id; drop batches whosequery_statusis notSUCCESS. - Broken stored state is
STATE_INCOMPATIBLEfor that query, not a silent new baseline. The run fails after writingquery_statusandrun_summary.
Homebrew count values arrive as comma-formatted strings ("251,930"). A
broken count is FAILED for that row, never silently 0.
Failure semantics
NO_RESULTS is a verified analytics items: []. It is never a transport
failure, and it is not "this watchlist id is unranked in a populated window".
HTML when JSON was expected, truncated JSON, HTTP 5xx, 429 after retry, or a
body over 8 MiB (SOURCE_LIMIT) are FAILED. The Actor run then fails
(SOURCE_FAILED) after writing query_status and run_summary. Do not treat
Apify SUCCEEDED as proof the window was usable: automation should key off
query_status. Missing formula metadata is HTML 404 from GitHub Pages; that is
item-level NOT_FOUND for that formula (rank still comes from analytics), not
an empty ranking. A metadata request that fails after retries is item-level
PARTIAL with error.code=METADATA_FAILED and null desc / homepage /
license; the window stays SUCCESS, state advances, and that row is not
charged. query_status carries not_found_count and metadata_failed_count.
This holds for top-N and watchlist queries alike, so a formula that was removed
from core but is still in the analytics window cannot stall a watch. Invalid input writes one query_status (INVALID_INPUT) and
a run_summary (INVALID_INPUT), then fails the run before any Homebrew
request. A spend cap that cannot cover the window writes NOT_PROCESSED and
does not fetch Homebrew.
maxFormulaRows never truncates a window: if the formula rows do not fit, you
get PARTIAL / OUTPUT_LIMIT and the previous state is kept. A cap below
topN (or below the watchlist length) is INVALID_INPUT before any request.
Default is 400 so a full top-200 window plus EXITED rows still fits.
Pricing
The Store Pricing tab is authoritative. Current pay-per-event rates:
| Event | When it is charged | FREE / BRONZE |
|---|---|---|
apify-actor-start | Platform start; one event at the default 512 MB. The Actor never emits it. | $0.00005 |
formula-result | Each delivered SUCCESS formula row in snapshot. In monitor, only NEW / RANK_* / EXITED / REAPPEARED; count drift is never charged. | $0.001 |
window-check | Monitor mode, fully delivered window, including NO_RESULTS and zero-change. Not charged for PARTIAL or FAILED. | $0.01 |
formula-result is $0.0008 on SILVER and $0.0006 on GOLD+. Platform usage is
included, not passed through. apify-default-dataset-item is off: enabling it
would double-bill every Dataset row.
A run cap must cover start plus the events: $0.02 is not enough for the sample
top-20, and $0.01 is not enough for a quiet monitor. The minimum run cap is
$0.021 so the Store prefill can finish. Snapshot of 20 formulae →
1 × $0.00005 start + 20 × $0.001 = $0.02005. Quiet or first monitor of the
same top-20 → start + $0.01 window-check = $0.01005 and $0 change rows. A
weekly top-20 monitor with, say, three rank moves → $0.01005 + 3 × $0.001 =
$0.01305; the worst case is every row moving, $0.03005. Retries, FAILED and
PARTIAL are not charged as formula-result or window-check (the platform
still charges start).
Private cloud measurement on 2026-09-11 (build 0.1.4, 512 MB): snapshot of
20 formulae → 1 × $0.00005 start + 20 × $0.001 = $0.02005 (contribution
margin ~78%). Quiet or first monitor of the same top-20 → start + $0.01
window-check = $0.01005 (~75%). A run cap must cover start plus the
events: $0.02 is not enough for the sample top-20. The minimum run cap is
$0.021 so the Store prefill can finish.
Limitations
- Cask-install analytics are out of scope.
- There is no native Homebrew history comparable to storing yesterday's JSON
yourself.
curlof the same public endpoint remains free. - Store search for this keyword family was in an EMPTY band at selection time. Do not read this README as evidence of measured demand.
- EXITED tombstones are kept 180 days, at most 20 000 per query. After prune, a
very old return may classify as
NEW. - MVP assumes one writer per
stateNamespaceand sequential runs. There is no distributed lock. - Optional metadata GETs are sequential. Metadata fields are omitted from the
ranking fingerprint and never decide the window status. Tap-qualified analytics ids (
user/tap/formula) have no Formulae/api/formula/{name}.jsonpage; metadata stays null for those rows.
Legal review on 2026-09-11: https://formulae.brew.sh/robots.txt has a
Sitemap and no Disallow. Analytics are anonymous aggregates. Re-read robots
and the Formulae API docs before Store
publication; a later ban is a stop, not something to bypass.
Use from AI agents with MCP
Connect the public Actor directly at
https://mcp.apify.com?tools=automa-flow/homebrew-formula-install-rankings-monitor.
Ask the agent to keep mode: monitor and outputMode: changesOnly on repeat
runs, to treat NO_RESULTS as a verified empty window, not a source
failure, and to treat FAILED / a failed run as an unusable Homebrew check.
This Actor is not targeted for MCP payment; do not treat that URL
as agentic billing readiness.
Schedule and webhook example
This is a copy-paste illustration, not a hosted integration promise. Homebrew rebuilds the window daily; weekly is a sensible cadence for rank moves.
Create the schedule (Console: Schedules, or the API; replace <ACTOR_ID> with
this Actor's ID and keep stateNamespace stable):
{"name": "homebrew-onrequest-30d-weekly","cronExpression": "0 7 * * MON","timezone": "UTC","isEnabled": true,"actions": [{"type": "RUN_ACTOR","actorId": "<ACTOR_ID>","runInput": {"body": "{\"mode\":\"monitor\",\"outputMode\":\"changesOnly\",\"stateNamespace\":\"onrequest-30d\",\"metric\":\"installOnRequest\",\"period\":\"30d\",\"topN\":20}","contentType": "application/json; charset=utf-8"},"runOptions": { "build": "latest", "timeoutSecs": 180, "memoryMbytes": 512, "maxTotalChargeUsd": 0.12 }}]}
Then add a webhook on ACTOR.RUN.SUCCEEDED that posts the run to n8n / Make /
your endpoint:
{"eventTypes": ["ACTOR.RUN.SUCCEEDED", "ACTOR.RUN.FAILED"],"condition": { "actorId": "<ACTOR_ID>" },"requestUrl": "https://your-endpoint.example/homebrew","payloadTemplate": "{\"runId\":{{resource.id}},\"status\":{{resource.status}},\"datasetId\":{{resource.defaultDatasetId}}}"}
In the receiver:
- Fetch
GET /v2/datasets/{datasetId}/items?clean=true. - Drop the batch if the
query_statusrow is notSUCCESS(includingPARTIALparse windows) or the run status isFAILED. - Keep rows where
record_type=formula_observationandchange_typeis one ofNEW,RANK_UP,RANK_DOWN,EXITED,REAPPEARED. Each row already carriesformula,rank,rank_previous,source_urlandevent_id, so it can be posted as an alert as-is; dedupe onevent_id. - Open
source_urlfor the human check. Do not readEXITEDas "deleted from core".
Protocol notes
User-Agent:
automa-flow-homebrew-formula-install-rankings-monitor/0.1 (+https://apify.com/automa-flow)https://formulae.brew.sh. Redirects off that host fail the window.
Analytics timeout 30 s; optional formula metadata timeout 20 s. Uncompressed
bodies over 8 MiB are FAILED / SOURCE_LIMIT.