# Wedding Vendor Lead Monitor — New Vendors Weekly, No Login (`flamboyant_liner/wedding-vendor-lead-monitor`) Actor

Watch Zola and Junebug Weddings directories by category and region and get only NEW wedding vendors since the last run: name, city, website, Instagram, phone and price range where published. Weekly schedule. No login. MCP-ready. $20 per 1,000 leads.

- **URL**: https://apify.com/flamboyant\_liner/wedding-vendor-lead-monitor.md
- **Developed by:** [Khrystyna Skotte](https://apify.com/flamboyant_liner) (community)
- **Categories:** Lead generation, Automation, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $20.00 / 1,000 new vendors

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/actors/running/actors-in-store.md#pay-per-event

## What's an Apify Actor?

An Actor is a serverless cloud program that runs on the Apify platform. It has two run modes.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.

Apify vocabulary and the platform model are defined once, in the agent quickstart at https://apify.com/agents.md.

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.

Do not guess an integration path. Every one of them is in the agent quickstart at https://apify.com/agents.md: the Apify MCP server, Agent Skills with the Apify CLI, the JavaScript and Python clients, the REST API, and the account-free path for an agent with no human to sign in. It also carries the rule on stating cost before the first paid run.

For examples already wired to this Actor's own input schema, see the [API](#api) section below.

Each client library has reference documentation the quickstart does not restate: [JavaScript/TypeScript](https://docs.apify.com/api/client/js/docs.md) (`npm install apify-client`) and [Python](https://docs.apify.com/api/client/python/docs.md) (`pip install apify-client`).

# README

## Wedding Vendor Lead Monitor — new vendors weekly from Junebug Weddings and Zola

Get a **weekly feed of only the wedding vendors that were newly listed since your last run** — photographers,
planners, venues, florists, DJs and more, filtered by category and state or country. The monitor reads the
complete vendor directories of **Junebug Weddings** (curated, worldwide) and **Zola** (~72,000 US vendors),
remembers every vendor it has already reported, and emits just the delta with website, Instagram, location,
price range, description and profile image. No login, no proxy needed.

A newly listed vendor is a fresh lead: they are actively investing in marketing, have no established referral
network yet, and are the most likely to buy.

### Who uses this

- **Vendor-to-vendor referrals**: planners, venues and photographers who want to meet new vendors in their market before everyone else does.
- **Wedding SaaS and service sales**: CRM, booking, album, website and insurance products sold to wedding professionals — new listings are new-business signals.
- **Directories and marketplaces**: keep your own vendor database current without re-scraping 70,000 profiles.
- **Market research**: track how fast each category and state is growing.

### How it works

1. Downloads each source's vendor sitemap (one request per source), which lists every vendor profile by category.
2. Compares the listing against the monitor's saved state (`source:category:slug → first seen date`).
3. For each unseen vendor, opens the profile page (`includeDetails`), applies the `regions` filter, and emits a
   `new_vendor` record.
4. Saves the state, then POSTs a run summary to `webhookUrl` if set.

Zola's sitemap is in publishing order, so the monitor checks the newest listings first; every emitted Zola
record carries `listedAt` (the date the storefront went live). Junebug does not publish listing dates.

State lives in a named key-value store `wedding-monitor-<hash of monitorId>` in your Apify account, capped at
100,000 vendors. Delete the store to reset a monitor.

### Input

| Field | Default | Notes |
|---|---|---|
| `categories` | `["wedding-photographers","wedding-planners"]` | See category slugs below; short forms like `photographers` work |
| `regions` | `[]` | US states as names or codes (`california`, `CA`, `new-york`), countries (`united-kingdom`, `mexico`, `canada`), or city slugs (`california/los-angeles` for Junebug, `los-angeles-ca` for Zola). Empty = worldwide |
| `sources` | `["junebug","zola"]` | Either or both |
| `includeDetails` | `true` | Fetch each new vendor's profile for website, Instagram, location, price, description, image. Zola always fetches details when `regions` is set (location is only on the profile) |
| `maxNewPerCategory` | `5` | Per source and category. New vendors beyond the cap stay unseen and come out next run |
| `maxItems` | `10` | Overall cap per run |
| `firstRunMode` | `emitAll` | `emitAll` emits a sample of current listings up to the caps and records everything else as seen; `baseline` records everything silently |
| `webhookUrl` | `""` | Optional POST target for the run summary |
| `monitorId` | `default` | One state store per ID — run several watchlists side by side |
| `proxyConfiguration` | none | Not needed. Set Apify residential proxy only if runs start returning HTTP 403 |

#### Category slugs

| Slug | Junebug | Zola |
|---|---|---|
| `wedding-photographers` | yes | yes |
| `wedding-planners` | yes | yes |
| `wedding-venues` | yes | yes |
| `wedding-videographers` | yes | yes |
| `wedding-florists` | yes | yes |
| `wedding-djs` | yes | yes (Zola "bands & DJs") |
| `wedding-musicians` | yes | yes (Zola "bands & DJs") |
| `wedding-hair-makeup` | yes | yes |
| `wedding-catering` | yes | yes |
| `wedding-cake-bakeries` | yes | yes (Zola "cakes & desserts") |
| `wedding-officiants` | yes | yes |
| `wedding-rentals`, `wedding-ring-stores`, `wedding-transportation`, `bridal-boutiques`, `custom-wedding-invitations` | yes | no |
| `wedding-bar-services`, `wedding-extras` | no | yes |

### Recommended setup for a weekly feed

1. Create a task with your categories and regions and set `monitorId` to something meaningful (`photographers-tx`).
2. **First run: set `firstRunMode` to `baseline`.** This records everything currently listed and emits nothing,
   so your first real delivery is not a backlog of thousands of established vendors.
3. Switch `firstRunMode` back to `emitAll` (it only matters while the state is empty anyway) and raise
   `maxItems` and `maxNewPerCategory` to a few hundred so a busy week is not cut off.
4. **Schedule the task weekly** (Zola adds a few dozen vendors a day across all categories; Junebug adds a
   handful a month). Daily works too and simply emits smaller batches.
5. Point `webhookUrl` at Slack (incoming webhook), Zapier, Make or your own endpoint.

The default settings (`{}`) run in `emitAll` mode and return a 10-vendor sample (5 Junebug, 5 Zola
photographers) so you see real output on the first try.

### Example: new photographers and planners in Texas, weekly

```json
{
  "categories": ["wedding-photographers", "wedding-planners"],
  "regions": ["texas"],
  "sources": ["junebug", "zola"],
  "includeDetails": true,
  "maxNewPerCategory": 200,
  "maxItems": 500,
  "firstRunMode": "baseline",
  "webhookUrl": "https://hooks.slack.com/services/XXX/YYY/ZZZ",
  "monitorId": "tx-photo-planners"
}
```

Other quick profiles:

- **New venues anywhere in the US**: `categories: ["wedding-venues"]`, `regions: ["usa"]`
- **Destination market watch**: `categories: ["wedding-planners","wedding-photographers"]`, `regions: ["mexico","italy","united-kingdom"]`, `sources: ["junebug"]`
- **Cheap URL-only feed** for your own enrichment pipeline: `includeDetails: false`, `regions: []`

### Output

One record per newly listed vendor:

```json
{
  "source": "zola",
  "vendorId": "b51102c9-7200-420e-a0bb-697ddfe49bfa",
  "slug": "stitch-in-time-photography-with-belinda-arnett",
  "name": "Stitch in Time Photography with Belinda Arnett",
  "category": "wedding-photographers",
  "location": "West Des Moines, IA",
  "city": "West Des Moines",
  "region": "Iowa",
  "country": "United States",
  "website": "https://www.stitchintimephotography.com",
  "instagram": "https://www.instagram.com/stitchintime_photography",
  "email": null,
  "phone": "(515) 822-5841",
  "priceRange": "Starts at $1,950",
  "description": "Hi! I'm Belinda, the face behind Stitch in Time Photography. …",
  "profileUrl": "https://www.zola.com/wedding-vendors/wedding-photographers/stitch-in-time-photography-with-belinda-arnett",
  "imageUrl": "https://images.zola.com/77a25aaa-bc9d-478d-bd1f-5744d297895d",
  "listedAt": "2026-09-25T21:40:49.899Z",
  "changeType": "new_vendor",
  "firstSeenAt": "2026-09-28T21:56:32.420Z",
  "monitorId": "default"
}
```

`email` and `phone` are filled only when the vendor publishes them on the profile (Zola shows phone for some
vendors; Junebug profiles use a contact form, so both are usually `null` there). `priceRange` and `listedAt`
are Zola-only. Junebug's `location` is the vendor's own service-area text, e.g. "Miami, Boca and Palm Beach,
Florida", while `city`, `region` and `country` come from the directory's placement.

### Webhook payload

POSTed once per run as `application/json`, also saved as the `SUMMARY` record in the run's key-value store:

```json
{
  "monitorId": "tx-photo-planners",
  "runAt": "2026-10-05T10:00:03.118Z",
  "newCount": 7,
  "scanned": 23041,
  "detailFetches": 9,
  "seenTotal": 23048,
  "baseline": false,
  "vendors": [ { "...first 50 records, same shape as the dataset..." } ]
}
```

### Pricing

Pay per event: a small start fee plus a per-vendor fee **only for new vendors emitted**. A weekly monitor that
finds nothing new costs just the start fee.

### Notes

- A run opens at most 200 profile pages per source/category pair. If a region is rare and the backlog is
  large, the remaining unseen vendors are checked on the following runs.
- Both directories answer requests from Apify's own IP ranges; no proxy is configured by default.
- Zola hosts US vendors only. Junebug covers the US, Canada, Europe, Latin America, Asia, Australia and Africa.

# Actor input Schema

## `categories` (type: `array`):

Directory categories to watch. Slugs: wedding-photographers, wedding-planners, wedding-venues, wedding-videographers, wedding-florists, wedding-djs, wedding-musicians, wedding-hair-makeup, wedding-catering, wedding-cake-bakeries, wedding-officiants, wedding-rentals, wedding-ring-stores, wedding-transportation, bridal-boutiques, custom-wedding-invitations (Junebug only), wedding-bar-services, wedding-extras (Zola only). Short forms like "photographers" or "planners" are accepted.

## `regions` (type: `array`):

Optional state / country / city filter. US states as names or codes ("california", "CA", "new-york"), countries as slugs ("united-kingdom", "mexico", "canada"), cities as the directory slugs ("california/los-angeles" for Junebug, "los-angeles-ca" for Zola). A vendor matches if any of its location tokens equals one of these. Empty = worldwide.

## `sources` (type: `array`):

Directories to monitor: "junebug" (Junebug Weddings, ~600 curated vendors worldwide) and/or "zola" (Zola marketplace, ~72,000 US vendors).

## `includeDetails` (type: `boolean`):

Open each new vendor's profile page to collect website, Instagram, location, price range, description and image. Turn off for a cheap URL-only feed (Zola always fetches details when a regions filter is set, because location is only on the profile).

## `maxNewPerCategory` (type: `integer`):

Stop after emitting this many new vendors for each source/category pair. New vendors beyond the cap stay unseen and come out on the next run.

## `maxItems` (type: `integer`):

Overall cap on records emitted in one run.

## `firstRunMode` (type: `string`):

What to do when this monitor has no saved state yet. emitAll: emit a sample of current listings (up to the caps) and record everything else as seen. baseline: silently record every current listing as seen and emit nothing, so the next scheduled run reports only vendors listed since.

## `webhookUrl` (type: `string`):

Optional. After each run a JSON summary {monitorId, runAt, newCount, vendors\[first 50]} is POSTed here (Slack/Zapier/Make/your API).

## `monitorId` (type: `string`):

Name of this watchlist. Each monitor ID keeps its own seen-state in a key-value store named wedding-monitor-<hash>, so you can run several profiles (e.g. "photographers-ca", "planners-uk") side by side.

## `proxyConfiguration` (type: `object`):

Optional. Not needed by default; both directories answer Apify's own IPs. Set Apify residential proxy only if runs start returning HTTP 403.

## Actor input object example

```json
{
  "categories": [
    "wedding-photographers",
    "wedding-planners"
  ],
  "regions": [],
  "sources": [
    "junebug",
    "zola"
  ],
  "includeDetails": true,
  "maxNewPerCategory": 5,
  "maxItems": 10,
  "firstRunMode": "emitAll",
  "webhookUrl": "",
  "monitorId": "default"
}
```

# Actor output Schema

## `records` (type: `string`):

Dataset of newly listed wedding vendors found in this run (JSON).

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "categories": [
        "wedding-photographers",
        "wedding-planners"
    ],
    "sources": [
        "junebug",
        "zola"
    ]
};

// Run the Actor and wait for it to finish
const run = await client.actor("flamboyant_liner/wedding-vendor-lead-monitor").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = {
    "categories": [
        "wedding-photographers",
        "wedding-planners",
    ],
    "sources": [
        "junebug",
        "zola",
    ],
}

# Run the Actor and wait for it to finish
run = client.actor("flamboyant_liner/wedding-vendor-lead-monitor").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print(f"💾 Check your data here: https://console.apify.com/storage/datasets/{run.default_dataset_id}")
for item in client.dataset(run.default_dataset_id).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "categories": [
    "wedding-photographers",
    "wedding-planners"
  ],
  "sources": [
    "junebug",
    "zola"
  ]
}' |
apify call flamboyant_liner/wedding-vendor-lead-monitor --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,flamboyant_liner/wedding-vendor-lead-monitor"
        }
    }
}
```

The hosted server signs you in with OAuth on first connect, so no API token belongs in this config. Clients without OAuth support can send an `Authorization: Bearer <APIFY_API_TOKEN>` header instead, using a token from API & Integrations in Apify Console (https://console.apify.com/settings/integrations).

## OpenAPI specification

Download the OpenAPI definition: https://api.apify.com/v2/actors/dhYszpKTIs7rbpu72/builds/7JsQrSDs6ckfFrFtV/openapi.json
