Pre-Black Friday: 20% off, for as long as you stayEARLYBFon Solo and AgencyEnds Nov 28Start free

How-to guide

How to Track Product Updates for Your Outbound (With a Script and One API)

Poll the changelogs of 50 target accounts, catch a launch the day it ships, and look up who to email in the same run.

Get your free API key →Free to start. No credit card. 1,000 records to spend whenever you like.

TL;DR

  • Product updates are public. Poll each target account's changelog or blog feed, flag new entries that match launch words, and you have the trigger.
  • LeadOcean does not detect launches. It answers the next question: who at that company should get the email. One search by domain returns them.
  • Setup takes about 45 minutes. Watching costs nothing. Each launch you act on spends roughly 5 to 10 LeadOcean records, so the 1,000 free records cover 100 or more launches.

Prerequisites

  • A LeadOcean API key in $LEADOCEAN_API_KEY (the free plan works: 1,000 records, no card).
  • Python 3.9 or newer. The script uses only the standard library.
  • A list of 20 to 100 target accounts with their domains.
  • Somewhere to send the result: a Slack webhook, a file or your sequencer's import.

Step 1: List accounts and where they publish

A launch shows up in one of three places: a changelog page, a blog or an RSS/Atom feed. Feeds are the cheapest to poll. Start with the ones that have a feed and add the rest later.

Save this as watchlist.json. The accounts below are placeholders.

json
[
  {"domain": "acme.com", "feed": "https://acme.com/changelog/feed.xml"},
  {"domain": "example.io", "feed": "https://example.io/blog/rss.xml"}
]

Check a feed exists before you add it. Open the page, view the source and look for a <link rel="alternate" type="application/rss+xml"> tag. Many changelog tools expose /feed.xml or /rss, but confirm each one.

Step 2: Poll the feeds and keep new launch entries

Run a script that fetches each feed, remembers what it has seen and prints only new entries with launch words in the title. State lives in a local file.

python
import json, re, urllib.request, xml.etree.ElementTree as ET

WORDS = re.compile(r"\b(launch|introducing|now available|new feature|released?|announcing|v\d+)\b", re.I)
seen = set(json.load(open("seen.json"))) if __import__("os").path.exists("seen.json") else set()
hits = []

for acct in json.load(open("watchlist.json")):
    req = urllib.request.Request(acct["feed"], headers={"User-Agent": "launch-watch/1.0"})
    root = ET.fromstring(urllib.request.urlopen(req, timeout=20).read())
    for item in root.iter():
        tag = item.tag.split("}")[-1]
        if tag not in ("item", "entry"):
            continue
        get = lambda n: next((c for c in item if c.tag.split("}")[-1] == n), None)
        title = (get("title").text or "").strip() if get("title") is not None else ""
        link_el = get("link")
        link = (link_el.text or link_el.get("href") or "").strip() if link_el is not None else ""
        if link and link not in seen and WORDS.search(title):
            hits.append({"domain": acct["domain"], "title": title, "url": link})
        seen.add(link)

json.dump(sorted(seen), open("seen.json", "w"))
json.dump(hits, open("hits.json", "w"), indent=2)
print(f"{len(hits)} new launch entries")

The first run marks every existing entry as seen but also reports old launch posts. Delete hits.json after run one, or run once with output discarded to seed seen.json.

Keep the word list tight. A loose list buries you in patch notes. If a team ships weekly, filter on introducing and launch only.

Step 3: Size the buyers for free

Before you spend a record, check how many people at that domain fit your target. The count call is free. Send count=true as a query parameter, not in the body.

bash
curl -s -X POST "https://api.leadocean.io/v1/people/search?count=true" \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": ["acme.com"],
    "seniority": ["vp", "head", "director"],
    "limit": 1
  }'

Read meta.total. If it is 0, skip the account. The total is capped at 100,000, which does not matter at one company.

Step 4: Pull the people who own the launch topic

Match the title to what shipped. A new API means engineering and product leads. A new reporting feature means ops or the department that reads reports. Filters are arrays and the body is flat JSON.

bash
curl -s -X POST "https://api.leadocean.io/v1/people/search" \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "domain": ["acme.com"],
    "title": ["Product", "Engineering"],
    "seniority": ["vp", "head", "director"],
    "emailStatus": ["verified", "catch_all_valid"],
    "limit": 5
  }'

This spends up to 5 records, one per person returned. Keep limit small. Five names per launch is enough for a two-person approach.

Enum values are exact strings. If a call returns nothing, check the allowed values with GET /v1/enums (free).

Step 5: Get the email and write the opener

Search results hold the profile. To get the address, enrich by the row's meta.source_ids.person_id with reveal_email set. Enrich takes exactly one of linkedin_url, email, phone or person_id.

bash
curl -s -X POST "https://api.leadocean.io/v1/people/enrich" \
  -H "x-api-key: $LEADOCEAN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{"person_id": "1234567", "reveal_email": true}'

Then build the opener from the hit, not from the person. The launch title is the reason for the email.

text
Subject: Saw {launch title}

Hi {first_name},

Saw {company} shipped {launch title} ({launch url}). Teams that add
this usually hit {the problem you solve} within a quarter.
Worth a 10-minute look?

Read email_status before sending. A bounced address is not refunded.

Step 6: Run it on a schedule

Chain steps 2 to 5 in one script and run it daily. Any scheduler works: cron, GitHub Actions or n8n.

bash
0 8 * * 1-5 cd /opt/launch-watch && python3 watch.py && python3 lookup.py

If you want an agent to do steps 3 to 5, connect the hosted MCP server at https://api.leadocean.io/mcp. It exposes 13 tools, including free leadocean_count_leads for the sizing step. See track job openings for the same pattern on hiring signals.

Which updates are worth a message

Not every release is a reason to write. Rank hits before they reach a person.

  • Launches of a new product or module. Highest value. The team has a new budget line and new problems.
  • Integrations and API releases. Good for tools that sell to developers or ops. The reader is technical, so link the changelog entry.
  • Pricing and plan changes. Strong if you sell a replacement or a cheaper option. Write the same day.
  • Bug fixes and patch notes. Skip them. The word filter in step 2 drops most of them.

A simple rule keeps volume sane: send at most one launch-based email per account per 30 days. Log the date in seen.json next to the link so the script can enforce it.

Common mistakes

Three things break this workflow in practice.

  1. Watching too many accounts at once. Start with 20 and tune the word list before you scale to 100.
  2. Searching the whole company. A launch about developer tooling should not reach the finance team. Match the title filter to the launch.
  3. Writing about the launch in general terms. The opener must name the exact feature, or it reads like a template.

What you get

The watcher prints one line and writes hits.json. A run looks like this (placeholder data).

json
[
  {
    "domain": "acme.com",
    "title": "Introducing Acme Workflows",
    "url": "https://acme.com/changelog/workflows"
  }
]

The people search returns the documented shape, abbreviated below (response shape from the API reference, placeholder values).

json
{
  "success": true,
  "data": [
    {
      "profile_data": {"profile_full_name": "Jane Doe", "profile_headline": "VP Product at Acme"},
      "contact_data": {
        "has_email": true,
        "email_status": "verified",
        "contact_current_experiences": [{"job_title": "VP Product", "company_name": "Acme", "company_domain": "acme.com"}]
      },
      "meta": {"source_ids": {"person_id": "1234567"}}
    }
  ],
  "meta": {"count": 1, "limit": 5, "nextCursor": null}
}

The person_id for step 5 sits at meta.source_ids.person_id on each row. Use fields to keep only what you need, for example fields: ["profile_data.profile_full_name", "meta.source_ids"].

Troubleshooting

ErrorCauseFix
401Missing or wrong x-api-keyCheck $LEADOCEAN_API_KEY is exported in the shell that runs the script.
429Requests above the plan's rate limit (5 a second on Free, 200 on Agency)Honour Retry-After and add a short sleep between accounts.
402Free records are spentUpgrade to Solo ($97 a month) or Agency ($497 a month). See pricing.
Empty dataAn enum value that does not match exactly, or no one at that domain fitsRun the free count first and check values with GET /v1/enums.
Feed parse errorThe URL returns HTML, not XMLOpen the URL in a browser and find the real feed link in the page source.
Hits flood after the first runseen.json was emptyRun once to seed state and discard that output.

FAQ

Does LeadOcean detect product launches itself?

No. LeadOcean holds people and company data. The launch signal comes from the feeds you watch. LeadOcean tells you who to contact once a launch fires.

What if a target has no feed?

Check for a changelog page and poll its HTML for new headings, or watch the company's release notes on its docs site. If neither exists, drop the account to a monthly manual check.

How many records does this spend a month?

It depends on how many launches you act on. At 5 records a launch and 20 launches a month, that is about 100 records. The Free plan holds 1,000 once, so it covers a trial of the whole workflow. Paid plans use a fair-use pace, not credits.

Can I use social posts instead of feeds?

Yes, and some vendors sell that as a keyword alert feed on public posts, for example Crustdata's write-up. The recipe above is self-run and free. A feed service saves the scripting.

Where does this fit with warm outbound?

A launch is a reason to write, which is what makes outbound warm. It sits inside the wider outbound lead generation process as the trigger step.

Find the buyer behind every launch

Free to start. No credit card. 1,000 records to spend whenever you like.

Get your free API key →