Home/Developers/Quickstart
5 minute read

Wine Pairing API Quickstart

From zero to a working wine pairing in five minutes. Send a sentence like “grilled salmon with lemon and dill” and get back ranked wine styles with a 0–100 match score. No setup, no SDK — just curl.

Updated April 16, 2026 · v1 API

What you will do

  1. Get an API key — 60 seconds
  2. Pick the right endpoint — decision tree
  3. Run three working examples — copy, paste, done
  4. Understand the score — what the numbers mean
  5. Use it in Claude Desktop — one-line MCP config
1

Get an API key

Sign up on the developer portal. You get 50 free calls per day, no credit card required.

Go to business.sommelierx.com, create an account, and copy your key. It starts with sk_live_ followed by 32 hex characters.

Export it as an environment variable so you can use it across the rest of this guide:

export SOMMELIERX_API_KEY="sk_live_..."
Tiers at a glance. FREE: 50 calls/day, basic match score. PRO: 500/day, full score breakdown plus three premium endpoints. ENTERPRISE: 10,000/day with SLA.
2

Pick the right endpoint

Most pairing tasks fit into one of six patterns. Find your input on the left, use the endpoint on the right.

I have… Use this endpoint Tier
A sentence: “grilled salmon with dill” POST /pairing/by-text FREE
A list of ingredient names: ["salmon", "dill"] POST /pairing/by-text
(use the ingredients array, skips the AI step)
FREE
A meal ID from our catalog POST /pairing/by-meal/{mealId} FREE
Ingredient IDs with amount and preparation POST /pairing/calculate FREE
A wine type ID (find dishes for it) GET /pairing/by-wine/{wineId}/meals FREE
A specific shortlist of wines to score POST /pairing/by-ingredients-wines PRO
Multiple courses, one wine to share POST /pairing/group PRO
An occasion tag (BBQ, romantic dinner, …) POST /pairing/by-occasion PRO
Don’t know the meal or wine ID? Use GET /search?q=… to look them up, or just send the dish name to /pairing/by-text — the API resolves ingredient names against the database for you.
3

Three working examples

Each block below is a complete request with a real, unedited response from api.sommelierx.com. Copy, paste, run.

Example A — Pair from a free-text dish

The simplest entry point: send a sentence in any language. The API extracts ingredients, resolves them to database IDs, and returns ranked wine styles.

curl -X POST https://api.sommelierx.com/api/v1/pairing/by-text \
  -H "Authorization: Bearer $SOMMELIERX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "text": "grilled salmon with lemon and dill",
    "language": "en"
  }'

Response (PRO tier, top 2 of 25 results shown):

{
  "data": {
    "resolved_ingredients": [
      { "input": "salmon", "matched": true, "ingredient": { "id": 6144, "name": "Salmon" } },
      { "input": "lemon",  "matched": true, "ingredient": { "id": 3656, "name": "Lemon"  } },
      { "input": "dill",   "matched": true, "ingredient": { "id": 3792, "name": "Dill"   } }
    ],
    "pairing_results": [
      {
        "metaWineId": 723,
        "name": "Semillon-Sauvignon New World Top",
        "type":   { "id": 4, "name": "White dry", "color": "white" },
        "region": "New World",
        "grapes": ["Sauvignon Blanc", "Sémillon"],
        "score": {
          "match_percentage": 88,
          "basic_score":      9,
          "balance_score":    0,
          "aromatic_score":   9
        },
        "priceSegment": "top",
        "whyText": "Sauvignon Blanc's herbaceous intensity amplifies dill's anise notes while Sémillon's waxy texture matches salmon's richness."
      },
      {
        "metaWineId": 1034,
        "name": "Arinto and Fernão Pires Portugal Basic",
        "type":   { "id": 4, "name": "White dry", "color": "white" },
        "region": "Portugal",
        "grapes": ["Arinto", "Fernão Pires", "Sauvignon Blanc", "Viognier"],
        "score": {
          "match_percentage": 88,
          "basic_score":      9,
          "balance_score":    0,
          "aromatic_score":   9
        },
        "priceSegment": "basic"
      }
    ]
  },
  "meta": {
    "tier": "pro",
    "calls_remaining_today": 4998,
    "rate_limit_reset": "2026-04-17T00:00:00.000Z"
  }
}

What you get back. Up to 25 wine styles ranked by match_percentage, filtered to a minimum of 65/100. Each result includes the wine’s name, color, region, grapes, a price segment hint (basic / middle / top), and on PRO tier a one-sentence whyText explaining the match.


Example B — Pair to a meal in our catalog

If you already have a meal ID from GET /meals or GET /search, skip the natural-language step.

curl -X POST https://api.sommelierx.com/api/v1/pairing/by-meal/5572 \
  -H "Authorization: Bearer $SOMMELIERX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{ "language": "en" }'

Response (meal 5572 = “Salmon sashimi”, top result shown):

{
  "data": {
    "mealId": 5572,
    "results": [
      {
        "metaWineId": 790,
        "name": "Chardonnay Southern Europe Middle",
        "type":   { "id": 4, "name": "White dry", "color": "white" },
        "region": "Southern Europe",
        "grapes": ["Chardonnay"],
        "glassType": { "id": 4, "name": "Burgundy White" },
        "score": {
          "match_percentage": 87,
          "basic_score":      9,
          "balance_score":    0,
          "aromatic_score":   9
        },
        "priceSegment": "middle",
        "whyText": "Southern Europe's warm-climate Chardonnay's mineral backbone amplifies the salmon's natural oils while respecting its pristine texture."
      }
    ]
  },
  "meta": {
    "tier": "pro",
    "calls_remaining_today": 4997,
    "rate_limit_reset": "2026-04-17T00:00:00.000Z"
  }
}

Example C — One wine across three courses PRO

Dinner party with a chicken starter, a vegetable main, and a steak? /pairing/group finds wines that score well across every meal, not just the average.

curl -X POST https://api.sommelierx.com/api/v1/pairing/group \
  -H "Authorization: Bearer $SOMMELIERX_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "mealIds": [4329, 4330, 4331],
    "language": "en"
  }'

Response (top wine shown):

{
  "data": {
    "meals": [
      { "id": 4329, "name": "Fried chicken with beurre blanc and leaf spinach" },
      { "id": 4330, "name": "Puff pastry tartlet with vegetables" },
      { "id": 4331, "name": "Fried Angus rib-eye with Café de Paris" }
    ],
    "wines": [
      {
        "metaWineId": 1152,
        "name": "Bobal Spain Basic",
        "type":   { "id": 10, "name": "Red dry", "color": "red" },
        "region": "Spain",
        "priceSegment": "basic",
        "avgScore":     75,
        "mealCoverage": 3,
        "totalMeals":   3,
        "perMealScores": [
          { "mealId": 4329, "mealName": "Fried chicken with beurre blanc and leaf spinach", "score": 74 },
          { "mealId": 4330, "mealName": "Puff pastry tartlet with vegetables",              "score": 75 },
          { "mealId": 4331, "mealName": "Fried Angus rib-eye with Café de Paris",           "score": 75 }
        ]
      }
    ]
  },
  "meta": {
    "tier": "pro",
    "calls_remaining_today": 4992,
    "rate_limit_reset": "2026-04-17T00:00:00.000Z"
  }
}

Read this carefully. mealCoverage = how many of the supplied meals scored above the threshold. avgScore = average across covered meals. A wine with mealCoverage: 2 may have a higher average than one with mealCoverage: 3 — sort by whichever matters more for your use case.

4

Understand the score

Every pairing returns a score object. On FREE tier you get match_percentage only. On PRO and ENTERPRISE you also see the three components that produced it.

match_percentage

integer 0–100

The headline number. Anything below 65 is filtered out. 80+ is a confident pairing; 90+ is exceptional.

basic_score

integer 0–10

Body, structure, and weight match. Does the wine have enough density to stand up to the dish without overwhelming it?

balance_score

integer (penalty)

Tannin/acid/sweetness clash penalty. Positive values reduce the final score by 0.5x; negative values reduce it by 1x. Lower (or zero) is better.

aromatic_score

integer 0–10 or null

Flavor-profile resonance — herbal, fruit, smoke, mineral. null means the algorithm didn’t use aromatic data for this pairing.

How they combine.

// 1. Average the basic and aromatic scores (or use basic only if aromatic is null)
final = aromatic_score !== null
  ? (basic_score + aromatic_score) / 2
  : basic_score

// 2. Apply the balance penalty
final = balance_score > 0
  ? final - (0.5 * balance_score)
  : final - (-1 * balance_score)

// 3. Scale to 0-100
match_percentage = Math.ceil(final * 10)
Why some wines score 0 on balance. balance_score: 0 means no clash — the wine’s tannin, acidity, and sweetness all sit comfortably with the dish. That’s a good thing: the final match is the average of basic and aromatic, with no penalty.
5

Use it in Claude Desktop, Cursor, or Windsurf

If you don’t want to write code, drop the SommelierX MCP server into any MCP-compatible client and ask in plain English.

Add this to your client’s MCP config (e.g. Claude Desktop’s claude_desktop_config.json):

{
  "mcpServers": {
    "sommelierx": {
      "command": "npx",
      "args": ["@sommelierx/mcp-server"],
      "env": {
        "SOMMELIERX_API_KEY": "sk_live_..."
      }
    }
  }
}

Restart your client and ask things like:

  • “What wine pairs with grilled lamb and rosemary?”
  • “I have a Barolo. What should I cook?”
  • “Suggest one wine for a three-course dinner: oysters, duck, chocolate cake.”

The SOMMELIERX_API_KEY env var is optional — without it you get the same FREE tier limits, just keyed to your IP.

What’s next

Errors you may hit

StatusCodeWhat it means
401API_KEY_MISSINGNo Authorization header. Pass Bearer sk_live_....
401API_KEY_INVALIDKey is malformed or doesn’t exist. Check for stray whitespace.
403TIER_REQUIREDYou called a PRO endpoint on a FREE key. The response includes upgrade_url.
429RATE_LIMIT_DAILYDaily quota exhausted. The X-RateLimit-Reset header tells you when it resets.
429RATE_LIMIT_MINUTEPer-minute burst exceeded (2/min on FREE, 20/min on PRO). Add a Retry-After backoff.
400INVALID_INPUTValidation failed. The details array lists the offending fields.

Ready to build?

Free key in under a minute. No credit card. 50 calls per day to start.