Skip to main content

How to send Browse AI data to Close CRM

M
Written by Melissa Shires

This guide covers three ways to send your Browse AI scraped data into Close CRM, from no-code automation to custom API integrations. Choose the method that best fits your team's technical comfort and use case.

πŸ“– Prerequisites: You'll need an approved Browse AI robot with scraped data and a Close CRM account. For API-based methods, you'll also need a Browse AI API key and a Close API key.

Which method should I use?

Here's a quick comparison to help you decide:

Method

Best for

Technical level

Speed

Zapier / Make

Teams without developers

No code

Near real-time

Webhooks

Real-time pipelines with custom logic

Intermediate

Real-time

API polling

Batch processing, scheduled syncs

Intermediate

On schedule

Method 1: Zapier or Make (no code)

The fastest way to connect Browse AI to Close. No coding required. Just map your scraped fields to Close leads and contacts.

Setting up with Zapier

  1. Go to zapier.com and create a new Zap.

  2. Trigger: Choose Browse AI as the trigger app, then select New Successful Task Finished as the event.

  3. Connect your Browse AI account and select the robot you want to sync data from.

  4. Action: Choose Close as the action app, then select Create Lead or Create Contact.

  5. Connect your Close account using your API key.

  6. Map your fields: Match each Browse AI captured field to the corresponding Close field. For example, map company_name β†’ Lead Name, email β†’ Contact Email.

  7. Test the Zap, then turn it on.

πŸ’‘ Tip: In Close, Contacts always belong to a Lead. If you use the Create Contact action without specifying a Lead ID, Close will automatically create a new Lead named after the contact. Use Find Lead first if you want to add contacts to existing leads.

Setting up with Make (formerly Integromat)

  1. Create a new scenario in Make.

  2. Add a Webhooks β†’ Custom webhook module as the trigger. Copy the webhook URL.

  3. In Browse AI, go to your robot's Integrate tab and add the Make webhook URL under Webhooks. Select the taskFinishedSuccessfully event.

  4. Add a Close β†’ Create a Lead module, connect your Close account, and map your fields.

  5. Activate the scenario.

Method 2: Webhooks (real-time, custom code)

Use Browse AI webhooks to push data directly into Close as soon as a task finishes. This gives you full control over data transformation and error handling.

How it works

  1. Browse AI sends a webhook POST request to your server when a task completes.

  2. Your server receives the scraped data and transforms it for Close's REST API.

  3. Your server calls Close's API to create or update leads and contacts.

Step 1: Get your Close API key

Close uses API keys with HTTP Basic Authentication (API key as username, empty password).

  1. In Close, go to Settings β†’ Integrations β†’ API Keys.

  2. Click + New API Key.

  3. Give it a name (e.g. "Browse AI Integration") and copy the key.

⚠️ Keep your API key secure. Store it in environment variables. Never hardcode it in your source code or commit it to version control.

Step 2: Build your webhook endpoint

This example receives Browse AI webhook data and creates a Lead with a nested Contact in Close:

import requests
from flask import Flask, request, jsonifyapp = Flask(__name__)CLOSE_API_KEY = "your_close_api_key"
CLOSE_API_URL = "https://api.close.com/api/v1"def create_close_lead(lead_data):
    """Create a lead in Close CRM."""
    resp = requests.post(
        f"{CLOSE_API_URL}/lead/",
        auth=(CLOSE_API_KEY, ""),
        json=lead_data
    )
    return resp.json()def search_lead_by_email(email):
    """Search for an existing lead by contact email."""
    resp = requests.get(
        f"{CLOSE_API_URL}/lead/",
        auth=(CLOSE_API_KEY, ""),
        params={"query": f"email:{email}"}
    )
    data = resp.json()
    leads = data.get("data", [])
    if leads:
        return leads[0]["id"]
    return [email protected]("/browse-ai-webhook", methods=["POST"])
def handle_webhook():
    payload = request.get_json()    if payload.get("event") != "taskFinishedSuccessfully":
        return jsonify({"status": "ignored"}), 200    task = payload["task"]
    captured = task.get("capturedTexts", {})    email = captured.get("email", "")    # Check if lead already exists
    existing_lead_id = None
    if email:
        existing_lead_id = search_lead_by_email(email)    if existing_lead_id:
        return jsonify({"status": "exists", "lead_id": existing_lead_id}), 200    # Build lead data with nested contact
    lead_data = {
        "name": captured.get("company_name", "") or f"{captured.get('first_name', '')} {captured.get('last_name', '')}".strip() or "Unknown",
        "url": captured.get("website", ""),
        "contacts": [{
            "name": f"{captured.get('first_name', '')} {captured.get('last_name', '')}".strip(),
            "title": captured.get("job_title", ""),
            "emails": [{"email": email, "type": "office"}] if email else [],
            "phones": [{"phone": captured.get("phone", ""), "type": "office"}] if captured.get("phone") else []
        }],
        "custom.cf_lead_source": "Browse AI"
    }    result = create_close_lead(lead_data)
    return jsonify({"status": "created", "lead_id": result.get("id")}), 200if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5000)

Step 3: Register the webhook in Browse AI

Via the dashboard:

  1. Open your robot and go to the Integrate tab.

  2. Under Webhooks, click Add webhook.

  3. Paste your endpoint URL (e.g. https://yourdomain.com/browse-ai-webhook).

  4. Select the taskFinishedSuccessfully event.

Via the API:

curl -X POST "https://api.browse.ai/v2/robots/YOUR_ROBOT_ID/webhooks" \
  -H "Authorization: Bearer YOUR_BROWSE_AI_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
    "url": "https://yourdomain.com/browse-ai-webhook",
    "events": ["taskFinishedSuccessfully"]
  }'

πŸ’‘ IP allowlisting: Browse AI sends webhooks from IP address 3.228.254.190. If your server has a firewall, add this to your allowlist. See Webhooks: IP address for allowlisting.

Method 3: API polling (batch/scheduled)

If you prefer to pull data on a schedule rather than receive it in real time, you can poll the Browse AI API for completed tasks and push results into Close in batches.

Example: Poll and sync to Close

import requests
from datetime import datetime, timedeltaBROWSE_AI_API_KEY = "your_browse_ai_api_key"
ROBOT_ID = "your_robot_id"
CLOSE_API_KEY = "your_close_api_key"def get_recent_tasks(since_hours=1):
    """Fetch tasks completed in the last N hours."""
    resp = requests.get(
        f"https://api.browse.ai/v2/robots/{ROBOT_ID}/tasks",
        headers={"Authorization": f"Bearer {BROWSE_AI_API_KEY}"},
        params={"pageSize": 100}
    )
    tasks = resp.json().get("result", {}).get("robotTasks", {}).get("items", [])    cutoff = datetime.utcnow() - timedelta(hours=since_hours)
    return [t for t in tasks if t.get("status") == "successful"
            and datetime.fromisoformat(t["finishedAt"].replace("Z","")) > cutoff]def sync_to_close(tasks):
    """Create Close leads from Browse AI task results."""
    for task in tasks:
        captured = task.get("capturedTexts", {})
        lead_data = {
            "name": captured.get("company_name", "Unknown"),
            "contacts": [{
                "name": f"{captured.get('first_name', '')} {captured.get('last_name', '')}".strip(),
                "emails": [{"email": captured.get("email", ""), "type": "office"}] if captured.get("email") else [],
                "phones": [{"phone": captured.get("phone", ""), "type": "office"}] if captured.get("phone") else []
            }]
        }        requests.post(
            "https://api.close.com/api/v1/lead/",
            auth=(CLOSE_API_KEY, ""),
            json=lead_data
        )# Run this script on a schedule (e.g. cron job every hour)

πŸ“– For full Browse AI API details, including pagination, bulk operations, and task filtering, see the API Guide: Getting started and API Guide: Bulk operations.

Close-specific tips

Understanding Close's data model

Close structures data differently from most CRMs:

  • Leads represent a company or organization. They're the top-level object.

  • Contacts are people within a lead. Each contact belongs to exactly one lead.

  • Opportunities are deals. They also belong to a lead.

When creating a lead, you can nest contacts directly in the request body (as shown in the examples above). This creates the lead and its contacts in a single API call.

Preventing duplicate leads

Close supports searching leads by email, phone, name, or any field using their query syntax:

# Search for existing lead by email
resp = requests.get(
    "https://api.close.com/api/v1/lead/",
    auth=(CLOSE_API_KEY, ""),
    params={"query": "email:[email protected]"}
)
existing = resp.json().get("data", [])
if existing:
    lead_id = existing[0]["id"]
    # Update instead of creating

Custom fields

Close supports custom fields on Leads, Contacts, and Opportunities. Custom field IDs follow the format custom.cf_XXXXX. To find your custom field IDs, call GET /api/v1/custom_field/lead/ (or /contact/ or /opportunity/).

Common field mappings

Here are typical mappings between Browse AI captured fields and Close fields:

Browse AI field

Close field

Notes

company_name

Lead name

Top-level lead name

website

Lead url

first_name + last_name

Contact name

Nested within the lead

job_title

Contact title

email

Contact emails

Array: [{"email": "...", "type": "office"}]

phone

Contact phones

Array: [{"phone": "...", "type": "office"}]

(origin URL)

Custom field or lead note

Available as task.inputParameters.originUrl

Sending monitoring data to Close

If you're using Browse AI monitors to track changes on websites, you can push change events into Close. Use the taskCapturedDataChanged webhook event to trigger updates. For example, you could add a note to a lead when a monitored page changes, or update a custom field when new pricing data is detected.

Troubleshooting

Close returns a 401 Unauthorized error

Check that you're using HTTP Basic Auth correctly. The API key goes as the username with an empty password. In Python requests, this looks like auth=(CLOSE_API_KEY, "").

Close returns a 429 rate limit error

Close rate limits vary by plan. Add retry logic with exponential backoff, and check the Retry-After header in the response for the wait time.

Contact created without a lead

If you create a contact without specifying a lead_id, Close automatically creates a new lead for it. To add a contact to an existing lead, find the lead first and pass its ID.

Webhook isn't firing

Make sure the webhook URL is publicly accessible and that your server responds with a 200 status code. See the Webhooks: Set up guide for detailed debugging steps.

Did this answer your question?