> ## Documentation Index
> Fetch the complete documentation index at: https://docs.mavera.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Review Themes → Local Content Strategy

> Extract review themes per location and generate location-specific marketing content with Mavera

## Scenario

Each location has distinct review themes — downtown customers love the espresso, suburban customers love the parking. You extract themes per location and generate location-specific marketing content that highlights what each location does best, in the language customers already use.

**Flow:** Per-location review themes → Mavera `POST /mave/chat` (theme extraction) → `POST /generations` (location content) → Per-location marketing assets

## Code

<CodeGroup>
  ```python Python theme={"dark"}
  import os, requests, time

  GOOG = os.environ["GOOGLE_ACCESS_TOKEN"]
  ACCT = os.environ["GOOGLE_ACCOUNT_ID"]
  MV = os.environ["MAVERA_API_KEY"]
  GB_BASE = "https://mybusiness.googleapis.com/v4"
  MV_H = {"Authorization": f"Bearer {MV}", "Content-Type": "application/json"}
  GB_H = {"Authorization": f"Bearer {GOOG}"}
  STAR_MAP = {"ONE": 1, "TWO": 2, "THREE": 3, "FOUR": 4, "FIVE": 5}

  locations = requests.get(f"{GB_BASE}/{ACCT}/locations",
      headers=GB_H, params={"pageSize": 50}).json().get("locations", [])

  for loc in locations[:5]:
      loc_id = loc.get("name", "")
      loc_name = loc.get("locationName", "Unknown")
      city = loc.get("address", {}).get("locality", "Unknown")

      r = requests.get(f"{GB_BASE}/{loc_id}/reviews",
          headers=GB_H, params={"pageSize": 50})
      if r.status_code != 200:
          continue
      reviews = r.json().get("reviews", [])

      positive = [rev.get("comment", "")[:250] for rev in reviews
                  if STAR_MAP.get(rev.get("starRating"), 3) >= 4 and rev.get("comment")]
      negative = [rev.get("comment", "")[:250] for rev in reviews
                  if STAR_MAP.get(rev.get("starRating"), 3) <= 2 and rev.get("comment")]

      # 1. Theme extraction
      themes = requests.post("https://app.mavera.io/api/v1/mave/chat", headers=MV_H,
          json={"message": f"""Extract themes from reviews for {loc_name} ({city}).

  POSITIVE ({len(positive)} reviews):
  {chr(10).join(positive[:10])}

  NEGATIVE ({len(negative)} reviews):
  {chr(10).join(negative[:10])}

  Return: top 5 positive themes, top 3 negative themes, each with frequency and representative quote."""}).json()

      print(f"\n{'='*50}")
      print(f"THEMES: {loc_name} ({city})")
      print(themes.get("content", "")[:500])

      # 2. Generate location-specific content
      gen = requests.post("https://app.mavera.io/api/v1/generations", headers=MV_H,
          json={"prompt": (
              f"Generate location-specific marketing content for {loc_name} in {city}.\n\n"
              f"Themes from customer reviews:\n{themes.get('content','')[:500]}\n\n"
              f"Create:\n"
              f"1. Google Business Profile post (150 words, highlight top strength)\n"
              f"2. Local Instagram caption (casual, with hashtags)\n"
              f"3. Location-specific tagline (10 words max)\n"
              f"4. Local SEO paragraph for website (100 words, include city name naturally)"
          )}).json()

      print(f"\nCONTENT:")
      print(gen.get("output", gen.get("content", gen.get("text", "")))[:600])
      time.sleep(1)
  ```

  ```javascript JavaScript theme={"dark"}
  const GOOG = process.env.GOOGLE_ACCESS_TOKEN;
  const ACCT = process.env.GOOGLE_ACCOUNT_ID;
  const MV = process.env.MAVERA_API_KEY;
  const GB_BASE = "https://mybusiness.googleapis.com/v4";
  const MV_H = { Authorization: `Bearer ${MV}`, "Content-Type": "application/json" };
  const GB_H = { Authorization: `Bearer ${GOOG}` };
  const STAR_MAP = { ONE: 1, TWO: 2, THREE: 3, FOUR: 4, FIVE: 5 };

  const locations = (await fetch(`${GB_BASE}/${ACCT}/locations?pageSize=50`, { headers: GB_H })
    .then((r) => r.json())).locations || [];

  for (const loc of locations.slice(0, 5)) {
    const locId = loc.name || "";
    const locName = loc.locationName || "Unknown";
    const city = loc.address?.locality || "Unknown";

    const res = await fetch(`${GB_BASE}/${locId}/reviews?pageSize=50`, { headers: GB_H });
    if (!res.ok) continue;
    const reviews = (await res.json()).reviews || [];

    const positive = reviews.filter((r) => (STAR_MAP[r.starRating] || 3) >= 4 && r.comment)
      .map((r) => r.comment.slice(0, 250));
    const negative = reviews.filter((r) => (STAR_MAP[r.starRating] || 3) <= 2 && r.comment)
      .map((r) => r.comment.slice(0, 250));

    // 1. Themes
    const themes = await fetch("https://app.mavera.io/api/v1/mave/chat", {
      method: "POST", headers: MV_H,
      body: JSON.stringify({
        message: `Themes for ${locName} (${city}):\n\nPOSITIVE:\n${positive.slice(0, 10).join("\n")}\n\nNEGATIVE:\n${negative.slice(0, 10).join("\n")}`,
      }),
    }).then((r) => r.json());

    console.log(`\n${"=".repeat(50)}\nTHEMES: ${locName} (${city})`);
    console.log((themes.content || "").slice(0, 500));

    // 2. Content
    const gen = await fetch("https://app.mavera.io/api/v1/generations", {
      method: "POST", headers: MV_H,
      body: JSON.stringify({
        prompt: `Marketing for ${locName} (${city}).\n\nThemes: ${(themes.content || "").slice(0, 500)}\n\nCreate: 1) GBP post (150 words) 2) Instagram caption 3) Tagline (10 words) 4) Local SEO paragraph (100 words)`,
      }),
    }).then((r) => r.json());

    console.log("\nCONTENT:");
    console.log((gen.output || gen.content || gen.text || "").slice(0, 600));
    await new Promise((r) => setTimeout(r, 1000));
  }
  ```
</CodeGroup>

## Example Output

```text theme={"dark"}
==================================================
THEMES: Downtown Café (Austin)
Positive: Espresso quality (18x), Fast service (14x), Staff friendliness (11x)
Negative: Crowding at noon (6x), No outdoor seating (4x)

CONTENT:

## Google Business Profile Post
The best espresso in downtown Austin — and your neighbors agree. Our baristas
pull 200+ shots daily, each one dialed in before the morning rush. Stop by
for your 11am pick-me-up. Lines move fast (we've heard you on the wait times).

## Instagram Caption
That 3pm espresso hit different when it's from downtown 🫠☕
Come see why Austin keeps coming back. Open 6am-8pm, every day.
#AustinCoffee #DowntownATX #EspressoLovers #ATXEats

## Tagline
"Downtown Austin's fastest espresso. No compromises."

## Local SEO
Looking for the best coffee in downtown Austin? Our Downtown Café
serves handcrafted espresso, cold brew, and pastries steps from
Congress Avenue. Austin locals rate us 4.6/5 on Google with 234 reviews.
```

## Error Handling

<AccordionGroup>
  <Accordion title="Locations with few reviews">Locations with fewer than 10 reviews produce weak themes. Skip or combine with nearby locations for analysis.</Accordion>
  <Accordion title="Multi-language reviews">In tourist-heavy locations, reviews come in multiple languages. Pass `?language=en` to filter, or let Mave handle mixed-language input.</Accordion>
</AccordionGroup>
