Skip to main content
Competitor signals go stale fast: a pricing page changes, and last quarter’s version is now the wrong answer that looks most like the right one. This cookbook builds a tracker around two ideas: a stable ID per page, so a new version replaces the old one, and a metadata filter per competitor, so one company’s signals never answer a question about another.

Prerequisites

  • A HydraDB API key from dashboard.hydradb.com, set as HYDRADB_API_KEY.
  • An OpenAI API key, set as OPENAI_API_KEY. HydraDB returns context, not answers, so any model works; swap the answer function below for your own.
  • Python 3.10 or later, with the SDKs: pip install "hydradb-sdk>=2.1.7,<3" openai.
Save this as hydra.py. Every cookbook imports it. It holds what every HydraDB application does around its own logic: wait for the database, check what an ingest accepted, wait for indexing, and pass query results to a model.
hydra.py

1. Create a database with a metadata schema

Declare the fields you will filter on when you create the database. Metadata filters work only on declared fields.
intel.py

2. Ingest signals with stable IDs

Send each page as an app source with the competitor and signal type in metadata. Build the id from the page, not from the day you fetched it: ingesting the same id again replaces the stored version, because upsert defaults to true.
intel.py

3. Query one competitor at a time

Filter on competitor so the two near-identical pricing pages cannot be confused. These are the KyroBench settings: hybrid matching, fast mode, eight results, graph context off, and a metadata filter for scope. In that report, removing the scope filter dropped recall from 0.917 to 0.185.
intel.py

4. Write the weekly digest

For a question that spans every signal type, keep the competitor filter, switch to thinking mode, and raise recency_bias so newer signals rank first.
intel.py

Run it

The pricing answer should give $24, not $20 and not Contoso’s $18. The digest should connect the price rise with the search hiring, and label the roadmap reading as inference.

Use real data

Run your fetcher on a schedule and call ingest with whatever it finds. Unchanged pages can be sent again safely. To be told when a batch finishes indexing instead of polling, register a webhook.

Common mistakes

  • Putting the date in the id: every fetch then adds a new source, and the stale versions compete with the current one.
  • Filtering on an undeclared field: the filter matches nothing. Declare the field in the metadata schema first.
  • A bare filter value: write {"competitor": {"equals": "northwind"}}. Filters without an operator are deprecated.

Next steps