Competitor tracking

Watch competitors' public footprint: blog and changelog feeds, GitHub releases, package downloads and where they are discussed on Hacker News.

The problem#

A competitor shows up in many small places: a blog feed, a release page, a package registry, a launch thread. Checking them all by hand every week is easy to skip.

Each of those is one Onekey call, so a daily digest is a short script you run on a schedule you control.

Endpoints to call#

EndpointUsed forCreditsStatus
web.feedsDiscover the RSS or Atom feed a company site advertises (once per competitor).2stable
web.feedParse the feed into normalised items: title, link, date.3stable
github.latest_releaseThe newest published release of a repository.1stable
npm.downloadsDownload count for an npm package over a period.1stable
hackernews.search_urlHacker News stories that link to a domain.5stable

Each endpoint name links to its reference page with every parameter, the caching window and a recorded response.

Your first call#

This is web.feed (3 credits). Replace YOUR_API_KEY with your key; sign up, no card. 1,000 free credits cover the open-platform endpoints. Hard-to-reach platforms (X, TikTok, Instagram, Reddit, and others marked on their page) need a verified email and a purchased pack.

curl "https://api.onekey.dev/v1/web/feed?url=https%3A%2F%2Fgithub.blog%2Ffeed%2F" \
  -H "x-api-key: YOUR_API_KEY"

A working script#

For each competitor, list the newest blog posts, the latest release, package downloads and recent Hacker News discussion. Store what you saw last run and print only the differences.

python
import os
import requests

API = "https://api.onekey.dev"
HEADERS = {"x-api-key": os.environ["ONEKEY_KEY"]}


def call(path, **params):
    """GET /v1/<platform>/<endpoint>; returns the data or raises with the error code."""
    r = requests.get(f"{API}/v1/{path}", params=params, headers=HEADERS, timeout=30)
    body = r.json()
    if not body["success"]:
        raise RuntimeError(f"{path}: {body['error']['code']}")
    return body["data"]


def get(item, field):
    """get(item, 'content.title'): a field of the unified schema, or None."""
    for key in field.split("."):
        item = (item or {}).get(key)
    return item


COMPETITORS = [
    {
        "feed": "https://competitor.example/blog/feed.xml",  # find it once: call("web/feeds", url="https://competitor.example")
        "owner": "competitor", "repo": "sdk",
        "npm": "competitor-sdk",
        "domain": "competitor.example",
    },
]

for c in COMPETITORS:
    for post in call("web/feed", url=c["feed"])[:5]:
        print("post   ", get(post, "content.createdAt"), get(post, "content.title"))
    rel = call("github/latest_release", owner=c["owner"], repo=c["repo"])
    print("release", get(rel, "content.createdAt"), get(rel, "content.title"))
    dl = call("npm/downloads", name=c["npm"])
    print("npm    ", get(dl, "entity.stats.downloads"), "downloads")
    for story in call("hackernews/search_url", q=c["domain"])[:5]:
        print("HN     ", get(story, "engagement.likes"), get(story, "content.title"))

What a month costs#

Illustrative volume, not a benchmark: 10 competitors checked once a day for 30 days (300 calls per endpoint), plus a one-off feed discovery for each.

EndpointCallsCredits per callCredits
web.feeds10220
web.feed3003900
github.latest_release3001300
npm.downloads3001300
hackernews.search_url30051,500
Total3,020

About 3,020 credits: the Starter pack (5,000 credits, $9) covers it, at $1.80 per 1,000 credits. Prices are in USD (regional pricing is planned); credits never expire. See pricing.

Every figure above assumes every call is a fresh fetch. These endpoints cache for 30 seconds to 1 hour. Free when served from cache (0.2 credit for a cached response over 100 KB). Repeating a call inside that window costs nothing. Invalid, failed and not-found calls are not charged either.

Good to know#

Keep reading#