Skip to main content

What is batch calling in Bolna?

Batch calling allows you to automate outbound calls to hundreds or thousands of contacts by uploading a CSV file with phone numbers and custom data. This feature is perfect for lead qualification, customer outreach, appointment reminders, and other high-volume calling campaigns.

How should I structure my batch CSV file?

  1. All phone numbers should include the country prefix in E.164 format
  2. All phone numbers should have contact_number as the header
  3. All other variables can be included in the CSV file in separate coloumns

example_batch_file.csv
contact_number,first_name,last_name
+11231237890,Bruce,Wayne
+91012345678,Bruce,Lee
+00021000000,Satoshi,Nakamoto
+44999999007,James,Bond

How to export a CSV file from Excel or Google Sheets?

In Excel, when you type a + at the beginning of a cell, Excel interprets it as a formula. To ensure the plus sign + is retained when entering phone numbers with country codes,
please add an apostrophe (') before the plus sign.
Download an example CSV file

How to use Batch APIs step by step?

i. Create a batch for agent

Once the CSV file is ready, upload it using the Create Batch API
curl --location 'https://api.bolna.ai/batches' \
--header 'Authorization: Bearer <api_key>' \
--form 'agent_id="aaaaaaaa-bbbb-cccc-dddd-eeeeeeeeeeee"' \
--form 'file=@"/my-first-batch.csv"'

ii. Scheduling the batch

After receiving your batch_id, you can schedule a batch using Schedule Batch API The scheduled date and time should be in ISO 8601 format with time zone.
curl --location 'https://api.bolna.ai/batches/abcdefghijklmnopqrstuvwxyz012345/schedule' \
--header 'Authorization: Bearer <api_key>' \
--form 'scheduled_at="2024-03-20T04:05:00+00:00"'

iii. Retrieving batch status

Check the status of the batch using Get Batch API
curl --location 'https://api.bolna.ai/batches/abcdefghijklmnopqrstuvwxyz012345' \
--header 'Authorization: Bearer <api_key>'

iv. Retrieving all batch executions

Once the batch has run, you can check all executions by the agent using List Batch Executions API
curl --location 'https://api.bolna.ai/batches/abcdefghijklmnopqrstuvwxyz012345/executions' \
--header 'Authorization: Bearer <api_key>'

Complete example: Building a batch calling application

batch_script.py
import asyncio
import os
from dotenv import load_dotenv
import aiohttp

# Load environment variables from .env file
load_dotenv()

# Load from .env
host = "https://api.bolna.ai"
api_key = os.getenv("api_key", None)
agent_id = 'ee153a6c-19f8-3a61-989a-9146a31c7834' #agent_id in which we want to create the batch
file_path = '/path/of/csv/file'
schedule_time = '2024-06-01T04:10:00+05:30'


async def schedule_batch(api_key, batch_id, scheduled_at):
    print("now scheduling batch for batch id : {}".format(batch_id))
    url = f"{host}/batches/{batch_id}/schedule"
    headers = {'Authorization': f'Bearer {api_key}'}
    data = {
        'scheduled_at': scheduled_at
    }

    try:
        async with aiohttp.ClientSession() as session:
            async with session.post(url, headers=headers, data=data) as response:
                response_data = await response.json()
                if response.status == 200:
                    return response_data
                else:
                    raise Exception(f"Error scheduling batch: {response_data}")
    except aiohttp.ClientError as e:
        print(f"HTTP Client Error: {str(e)}")
    except Exception as e:
        print(f"Unexpected error: {str(e)}")


async def get_batch_status(api_key, batch_id):
    print("now getting batch status for batch id : {}".format(batch_id))
    url = f"{host}/batches/{batch_id}"
    headers = {'Authorization': f'Bearer {api_key}'}

    try:
        async with aiohttp.ClientSession() as session:
            async with session.get(url, headers=headers) as response:
                response_data = await response.json()
                if response.status == 200:
                    return response_data
                else:
                    raise Exception(f"Error getting batch status: {response_data}")
    except aiohttp.ClientError as e:
        print(f"HTTP Client Error: {str(e)}")
    except Exception as e:
        print(f"Unexpected error: {str(e)}")


async def get_batch_executions(api_key, batch_id):
    print("now getting batch executions for batch id : {}".format(batch_id))
    url = f"{host}/batches/{batch_id}/executions"
    headers = {'Authorization': f'Bearer {api_key}'}

    try:
        async with aiohttp.ClientSession() as session:
            async with session.get(url, headers=headers) as response:
                response_data = await response.json()
                if response.status == 200:
                    return response_data
                else:
                    raise Exception(f"Error getting batch executions: {response_data}")
    except aiohttp.ClientError as e:
        print(f"HTTP Client Error: {str(e)}")
    except Exception as e:
        print(f"Unexpected error: {str(e)}")


async def create_batch():
    url = f"{host}/batches"
    headers = {'Authorization': f'Bearer {api_key}'}

    with open(file_path, 'rb') as f:
        form_data = aiohttp.FormData()
        form_data.add_field('agent_id', agent_id)
        form_data.add_field('file', f, filename=os.path.basename(file_path))

        async with aiohttp.ClientSession() as session:
            async with session.post(url, headers=headers, data=form_data) as response:
                response_data = await response.json()
                if response_data.get('state') == 'created':
                    batch_id = response_data.get('batch_id')
                    res = await schedule_batch(api_key, batch_id, scheduled_at=schedule_time)
                    if res.get('state') == 'scheduled':
                        check = True
                        while check:
                            # Checking the current status every 1 minute
                            await asyncio.sleep(60)
                            res = await get_batch_status(api_key, batch_id)
                            if res.get('status') == 'completed':
                                check = False
                                break
                    if not check:
                        res = await get_batch_executions(api_key, batch_id)
                        print(res)
                        return res


if __name__ == "__main__":
    asyncio.run(create_batch())

Next steps

Ready to implement batch calling? Start by creating your first batch via the API or explore related features: For high-volume needs, consider the Enterprise Plan with elevated concurrency limits and priority processing.
I