---
title: "Fitting counterparty models from public APIs"
description: "How to fit parametric models of negotiation behavior using grid search over public feeds, your own conversations, and leave-one-out validation."
date: 2026-10-05
canonical: https://pabloalbaladejo.com/blog/reverse-engineering-counterparty-models-from-public-apis
---

# Fitting counterparty models from public APIs

When you face a negotiating counterparty with no source code and no documentation of their decision rules, what you get is observable behavior: they open at a certain price, they concede in patterns, they have limits and patience bounds. If you collect enough observations, you can fit a parametric model to predict their next move.

Below is the approach I used to fit dealer models during a tournament, using only the public API feed (structure of all teams' conversations), my own negotiations, and grid search over candidate parameters. The method works on any counterparty whose behavior is observable and partially repetitive.

## The setup: what's public, what you control

The Bazaar API makes three things visible:

**Public feed** (`GET /api/feed?limit=500`) — every team's thread with every counterparty, structure visible: who said what, price changes, accept or reject. Message text arrives empty; you work from structure only.

**Dealer list** (`GET /api/dealers`) — each dealer's opening price for each card, rarity, and set. This is the surface. The hidden layer is how much they concede, how long they wait, what their floor or ceiling is.

**Your own threads** — you control both sides. You see your offers, their responses, and outcomes: accepted, walked, or final offer. This is your richest data.

What you cannot see: a dealer's secret floor or ceiling, their patience (message count before final offer), or whether they remember how you treated them. These hidden parameters drive behavior.

## The model: four parameters

One dealer had these observable parameters:

**Opening markup** — she opens higher than her minimum. You see the opening price from the API. Hypothesis: she sets a secret floor, then marks it up.

**Concession rate** — how much she yields per message. Hypothesis: some dealers concede a fixed fraction per message; others respond to your step size.

**Patience** — how many messages before she gives a final offer.

**Welcome bonus** — whether new customers get a better first offer than repeat customers.

The model is: simulate the negotiation thread by thread, apply the concession pattern, check for patience limits, and see if predicted final price matches observed.

## Fitting with grid search

**Data**: 29 own conversations (78 observed prices), plus the public feed.

**Grid search**: Try every combination of (opening_markup, concession_rate, patience, welcome_bonus) from a coarse grid. For each parameter set, run it against your conversations and count matches: did simulated final price match observed? Rank by accuracy.

**Validation**: Leave-one-out cross-validation. Fit on N-1 conversations, test on the one held out, repeat for all N. One dealer achieved 79% exact predictions with mean absolute error 0.32 primas. That means: about one miss in five, and when there is a miss, the error is on average 0.32 primas.

## Why this works, and when it doesn't

**It works when**: The counterparty's behavior is partially repetitive. The public feed shows dealers responding consistently across teams with similar concession patterns. In my negotiations, I observed this consistency: dealers who conceded aggressively with one team did so with others too. So they follow consistent patterns, which makes them predictable.

**It doesn't when**: The counterparty adapts to you personally, their behavior is random, or the feed shows contradictions.

**Limits**: The model predicts final-offer price, not opening. It assumes patience is constant. Most importantly, 79% fit means one in five predictions misses. Good enough to route decisions, not enough to claim full understanding. Treat it as a heuristic.

## Validating the model

When you have fit the model, you have two choices:

- **Test on new conversations** you generate yourself. Collect 10 new conversations with the counterparty, refit on N-1, test on the held-out one, repeat. This is cheap and gives you a fresh LOO score.
- **Monitor live performance.** If your routed decisions consistently beat your expected value, the model is useful. If not, refit.

The discipline is not in the grid search code. It is in treating a hidden counterparty as something you can learn from empirically, and in staying honest: 79% means you will be wrong, and that is the whole point.

## Related writing

- [Observability for LLM pipelines](https://pabloalbaladejo.com/blog/observability-for-llm-pipelines) — fitting models of system behavior from telemetry applies to counterparties too.
- [GEO: how AI agents find you](https://pabloalbaladejo.com/blog/geo-how-ai-agents-find-you) — modeling agents via public APIs, but for discoverability instead of negotiation.

## FAQ

### What is a counterparty model?

A parametric model of how another agent or player makes decisions: what price they offer, how they concede, when they walk. You fit it by observing their behavior with you and with others via public data, then validate it on unseen cases.

### Can you fit a model when the counterparty is a black box?

Yes. If the counterparty produces observable outputs to inputs you control, you can grid search over parameters that explain the pattern. Leave-one-out cross-validation shows which parameters generalize.

### What if the counterparty's behavior changes mid-game?

Your model captures one snapshot. Refit periodically using recent data. A model that gets the exact price in about four cases out of five (77–79% in my two fits) is honest about its limits: good enough to route decisions, not good enough to claim full understanding.

### Why use public feed data if the counterparty talks to many teams?

The counterparty's response structure should be consistent across teams or vary only by visible factors. Your own conversations validate what you see in the feed. The feed gives you a larger sample than you could generate alone.

## Sources

- [Bazaar API specification and dealer behavior](https://bazaar.causaprima.ai/docs)
- [/api/feed — public endpoint, all teams' conversation threads with structure visible](https://bazaar.causaprima.ai/api/feed)
- [/api/dealers — public dealer list and opening prices](https://bazaar.causaprima.ai/api/dealers)
- [Leave-one-out cross-validation methodology](https://en.wikipedia.org/wiki/Leave-one-out_cross-validation)
