Build a Company List Under Budget

Build a deduplicated company CSV with controlled Desearch API spend.

Introduction

30 Second Summary

Building a company list can become an open-ended search with an open-ended bill. Repeated companies make the final list harder to trust.

In this project, you will use the Desearch Company Search API to turn a plain-language brief into a capped list of companies with contacts and checked emails. You will control the cost of each run while preventing duplicate companies across searches.

What You'll Build

You'll open a CSV containing company names, leaders, and checked emails, with no repeated domains and a cost record for every search.

By the end of this project, you'll have:

  • An under-budget company search that limits how many companies a job can return. You'll see the reserved cost before reading the final charge.
  • A resumable search workflow where a paused job continues without a new charge. Repeating the original request also returns the same job.
  • A deduplicated company list that excludes every domain from the first run. You'll compare both exports to confirm that no company appears twice.
  • Secret Mission: An optional challenge to push your skills further.

What do I need before starting?

No coding experience is required. You'll use a Desearch account, an API key, curl, and a spreadsheet app.

Before We Start

The Desearch Company Search API runs each company search as a background job. You need a reliable way to send requests from your computer.

The results can be exported as CSV files. You need a spreadsheet application to inspect those exports.

In this step, you'll prepare curl for the requests. You'll also confirm that a spreadsheet application is ready for the results.

In this step, get ready to:
  • Verify curl in a terminal.
  • Prepare a spreadsheet application for CSV files.
  • Confirm that both local tools are ready.
Check curl in a terminal

curl sends HTTP requests from a command line. It lets you call the API without building an application first.

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type Terminal (macOS) or PowerShell (Windows) into the search bar.
  • Press Enter to open the terminal.
  • Check whether curl is available by running this command:
curl --version

What does this command do?

The --version option asks curl to identify its installed version. The output also lists the features supported by that installation.

✔️ I see curl version details

Your output starts with curl followed by a version number. This confirms the command-line tool is ready.

ⓧ curl is unavailable

The download table is dense, so finding the right package can feel fiddly. Match the package to your operating system before checking that its type is listed as a command-line tool.

  • Visit the official curl download page.
  • Find the section for your operating system.
  • Choose a command-line tool package that matches your computer's processor.
  • Follow the package provider's installation instructions.
  • Close your terminal after the installation finishes.
  • Reopen the terminal through your operating system's search bar.
  • Check the installation by running this command:
curl --version

What should I see?

You should now see output beginning with curl followed by a version number. That first line confirms your terminal can find the installation.

Still unable to run curl?

Close every terminal window after installing curl. Reopen the terminal so it reloads your system settings.

On Windows, PowerShell can interpret the curl name differently. Use the executable name directly to check whether curl is available.

curl.exe -V

This command calls the Windows curl executable directly. You should see its version details in the terminal.

Ask for help with your specific operating system: Help me make curl available in my terminal.

Prepare a spreadsheet application

A spreadsheet application turns a CSV export into visible rows and columns. This makes company names, leaders, domains, and checked emails easier to review.

Which setup matches your computer?

✔️ I already have a spreadsheet app

  • Press Cmd+Space (macOS) or the Windows key (Windows) to open your search bar.
  • Type the name of your spreadsheet application into the search bar.
  • Press Enter to open the application.
  • Create a blank spreadsheet from the application's start screen.

You should see an empty grid with labelled rows and columns. This confirms the application is ready to display a CSV export.

ⓧ I need a spreadsheet app

LibreOffice includes Calc, a spreadsheet application that can open CSV files. Use the official installer for your operating system.

macOS

  • Download the main package from the official LibreOffice download page.
  • Choose the Apple Silicon package if your Mac uses an Apple processor.
  • Choose the Intel package if your Mac uses an Intel processor.
  • Open the downloaded .dmg file.
  • Drag the LibreOffice icon onto the Applications icon in the installation window.
  • Approve the administrator request if macOS displays one.

Installation can sit on the progress bar for a moment. That pause means macOS is copying the application files.

  • Press Cmd+Space to open Spotlight.
  • Type LibreOffice into Spotlight.
  • Press Enter to open the LibreOffice Start Center.
  • Click Calc to create a blank spreadsheet.

You should see the Calc window with an empty grid of cells. The spreadsheet application is ready for your later CSV export.

Windows

  • Download the main package from the official LibreOffice download page.
  • Open the downloaded installer from your Downloads folder.
  • Choose the default setup in the installation wizard.
  • Continue through the installation wizard.
  • Click Install when the wizard is ready.
  • Approve the permission request if Windows displays one.
  • Click Finish after the installation completes.

The installer has added LibreOffice to your applications. You can now open its spreadsheet component.

  • Press the Windows key to open search.
  • Type LibreOffice into the search bar.
  • Press Enter to open the LibreOffice Start Center.
  • Click Calc to create a blank spreadsheet.

You should see the Calc window with an empty grid of cells. The spreadsheet application is ready for your later CSV export.

Don't see a blank spreadsheet?

Make sure you opened the spreadsheet component of your application. A document editor or presentation editor shows a different workspace.

If you installed LibreOffice, open its Start Center before selecting Calc.

Ask for help with the application on your computer: Help me open a blank spreadsheet.

Confirm both tools are ready

Your setup is ready when the terminal can identify curl. Your spreadsheet application must also show a blank cell grid.

Before the final check, what two visible signs do you expect to confirm your setup?

  • Recheck curl in the terminal from earlier by running this command:
curl --version

What should I see?

You should see a line beginning with curl followed by its version number. This proves the terminal can run curl.

  • Switch back to the spreadsheet application from earlier.
  • Confirm that the blank grid shows rows and columns.

The version output confirms curl is available. The blank grid confirms your spreadsheet application is ready for CSV exports.

That's the local setup sorted. Next, you'll create your Desearch access and send a capped company search request.

Start a Capped Company Search

Each Desearch Company Search API task reserves spend when it starts. A result cap sets the largest possible charge before the search begins.

In this step, you prepare authenticated access to Desearch. You start Job 1 with a five-company cap that keeps the reservation under budget.

In this step, get ready to:
  • Create your Desearch account.
  • Store your API key securely.
  • Start Job 1 with a five-company result cap.
Create your Desearch account and API key

The Desearch Console manages your account. It also shows the API credits available for starting jobs.

  • Visit the Desearch Console.
  • Select Sign up.
  • Complete the sign-up flow until the Desearch dashboard loads.

Attaching a card enables pay-as-you-go billing. New accounts receive $5 in API credits after the card is attached.

Your five-company request can reserve no more than $0.225. That gives this first paid action a clear ceiling.

  • Select Pricing and Billing in the Console.
  • Follow the payment form to attach a card.
  • Confirm your Console balance shows $5 in API credits.

Your account now has enough credit for the capped jobs in this project.

The API key authorizes requests from your terminal. Desearch expects it in the Authorization header.

The full key may only appear once, so this part deserves a careful pause. Saving it in a password manager keeps it out of screenshots and project files.

  • Select API Keys in the Console.
  • Click Generate API Key.
  • Enter local-dev in the Name field.
  • Confirm the key creation.
  • Copy the generated key once.
  • Store the copied key in your password manager with the label Desearch API credential.

Your Desearch credential is now stored away from the files and screenshots you create later.

Load your key and start Job 1

An environment variable lets curl read your key without placing the secret inside the request command. The value stays available in this terminal session.

  • Switch back to the terminal from earlier.
  • Copy the Desearch key from your password manager.
  • Load the key into this terminal by running the commands below:
printf "Paste your Desearch API key: "
read -s DESEARCH_API_KEY
export DESEARCH_API_KEY
printf "\nDESEARCH_API_KEY is loaded for this terminal.\n"

What do these commands do?

  • The hidden input reads your key without displaying its characters.
  • The export command makes DESEARCH_API_KEY available to curl.
  • The final message confirms that the terminal reached the end of the setup.

You should see DESEARCH_API_KEY is loaded for this terminal. Your actual key remains hidden.

Didn't see the loaded message?

Run the commands again in a Bash-compatible terminal. Paste the complete key when the prompt waits for input.

If the prompt returns immediately, copy the key again from your password manager before retrying.

Help me load my Desearch API key securely.

Company Search charges $0.045 for each company delivered. Setting count to 5 limits this request to a $0.225 reservation.

This starts a paid background job. The cap keeps the reservation inside the amount you reviewed before running the request.

Before you run it, what value do you expect the five-company cap to place in reserved_cost_usd?

  • Start Job 1 by running this request:
curl --request POST 'https://api.desearch.ai/desearch/companies/search' \
  --header "Authorization: $DESEARCH_API_KEY" \
  --header 'Content-Type: application/json' \
  --header 'Idempotency-Key: nextwork-job-1' \
  --data '{
    "query": "Series A fintech startups in Berlin building payment infrastructure",
    "count": 5,
    "filters": {"countries": ["DE"], "require": ["email"]}
  }' \
  --output job-1-start.json

What does this request do?

  • The query describes the target companies in plain language.
  • The count value caps Job 1 at five delivered companies.
  • The filters value keeps the search in Germany.
  • The require value limits delivery to companies with an email.
  • The Idempotency-Key identifies this specific start request.
  • The --output option saves the response in job-1-start.json.

Curl shows its transfer progress while Desearch accepts the request. The response is saved without exposing your API key.

Record the Job 1 response

The start response contains the identifier required by every later Job 1 request. It also records the amount reserved for the capped search.

Before you inspect the saved response, which two fields should prove that Job 1 exists within the expected spend cap?

  • Display the saved Job 1 response by running this command:
cat job-1-start.json

What does this command do?

The cat command prints the saved JSON response in your terminal. This confirms that job-1-start.json contains the original start response.

You should see a unique task_id plus reserved_cost_usd set to 0.225. The response should also show Job 1 starting with a queued status.

  • Record the value beside task_id here: your Job 1 task ID.
  • Record the value beside reserved_cost_usd here: your Job 1 reserved cost in USD.

Response missing the Job 1 fields?

  • Check for a 401 or 403 response that points to a missing or invalid API key.
  • Check for a 402 response that points to insufficient credits for the reservation.
  • Check for a 422 response that points to an invalid request value.

Help me troubleshoot my Job 1 start response.

✔️ Awesome, I've got everything!

Great. Job 1 is running with a five-company cap.

Keep job-1-start.json in place because the next step uses its task details.

ⓧ I'd like to double check the full code

Desearch API Key: Created in the Desearch dashboard and stored securely for authenticated API requests.

Job 1: Company Search job started with the chosen plain-language company description and a capped maximum number of returned companies. Its job ID and reserved_cost_usd from the start response have been recorded.

Job 1 Start Response: Saved response containing the original Job 1 job ID and reserved_cost_usd.

That is the spend guardrail in place. Next, you will poll Job 1 until it finishes and compare the reservation with the final charge.

Poll the Job and Record Its Cost

Your first Desearch Company Search job is now working through your plain-language company description. The result cap keeps its maximum spend bounded.

A reservation sets the spending ceiling for this run. The completed response reveals the final charge plus any returned amount.

In this step, get ready to:
  • Poll Job 1 until its status is completed.
  • Save the completed task response.
  • Calculate the actual charge in a cost log.
Poll Job 1 until it completes

Polling reads the latest state of the background job. Each response shows its current status without adding another charge.

  • Capture the Job 1 ID from your earlier record here: your Job 1 job ID.

Before you poll, do you think Job 1 has finished or is still working?

  • Read the latest Job 1 state by running this command in the terminal from earlier:
curl --request GET "https://api.desearch.ai/desearch/companies/tasks/[[JOB_1_ID="your Job 1 job ID"]]" \
  --header "Authorization: $DESEARCH_API_KEY"

What does this command do?

  • The GET request reads the task identified by your Job 1 ID.
  • The Authorization header reuses your securely stored Desearch API credential.
  • Polling reads the task state for free.

You'll see a JSON task object. Its status shows queued, running, or completed.

A background search can still be working when you first check. A running status confirms that the task is active.

  • Wait a few seconds if the status is queued or running.
  • Press the Up Arrow key to reload the polling command.
  • Press Enter to poll again.
  • Repeat the check until the status is completed.

You have reached the completed task state. The response now includes final_cost_usd plus refunded_usd.

Can't read Job 1?

  • Confirm that the Job 1 ID matches the task_id from the start response.
  • Confirm that the terminal still has access to the securely stored Desearch API credential.
  • Check whether the response ended with a status other than completed.

Help me diagnose my Job 1 polling response.

Save the completed task response

The completed response is your billing record for Job 1. Saving it preserves the final cost fields after the terminal output scrolls away.

  • Save the completed response to your Desktop by running this command:
curl --request GET "https://api.desearch.ai/desearch/companies/tasks/[[JOB_1_ID="your Job 1 job ID"]]" \
  --header "Authorization: $DESEARCH_API_KEY" \
  --output ~/Desktop/job-1-completed.json

What does this command do?

  • The request reads the completed Job 1 task again.
  • The --output option saves the response body as job-1-completed.json on your Desktop.
  • The saved response contains the final cost plus the refunded amount.
  • Confirm that the completed-response file exists by running:
ls ~/Desktop/job-1-completed.json

What does this command confirm?

  • The ls command checks the exact Desktop path.
  • A successful check prints the saved file path.

You'll see ~/Desktop/job-1-completed.json in the terminal output. Your completed-task response is now saved.

Completed response file missing?

  • Check that the save command includes --output before the Desktop path.
  • Confirm that the polling response showed completed before saving it.
  • Run the save command again if the earlier request was interrupted.

Help me save my completed Desearch task response.

Build the Job 1 cost log

The reservation covered the maximum result count. Desearch returns the unused portion when the task ends.

Your actual charge equals the reservation minus the refund. That calculated value should match final_cost_usd.

  • Display the saved completed response by running:
cat ~/Desktop/job-1-completed.json

What should you record?

  • The saved response repeats the Job 1 task_id.
  • The final_cost_usd field shows the amount billed for delivered companies.
  • The refunded_usd field shows the returned part of the reservation.
  • Capture reserved_cost_usd from your earlier start response here: your Job 1 reserved cost.
  • Capture final_cost_usd from the completed response here: your Job 1 final cost.
  • Capture refunded_usd from the completed response here: your Job 1 refunded amount.
  • Return to the spreadsheet application from earlier.
  • Create a blank spreadsheet for the Job 1 cost log.
  • Add the five cost-log headers by pasting this row into cell A1:
job_id	reserved_cost_usd	final_cost_usd	refunded_usd	actual_charge_usd

How is the cost log organised?

  • The first four columns preserve identifiers plus values from the two API responses.
  • The final column holds your independent actual-charge calculation.
  • Save the spreadsheet on your Desktop with the name company-search-cost-log.
  • Confirm that row 1 shows five separate headers.
  • Populate cells A2 through D2 by pasting this row into cell A2:
[[JOB_1_ID="your Job 1 job ID"]]	[[JOB_1_RESERVED_COST_USD="your Job 1 reserved cost"]]	[[JOB_1_FINAL_COST_USD="your Job 1 final cost"]]	[[JOB_1_REFUNDED_USD="your Job 1 refunded amount"]]

What does this row preserve?

  • The Job 1 ID connects the cost row to its saved response.
  • The reserved value comes from the start response.
  • The final value plus the refund come from the completed response.
  • Select cell E2.
  • Calculate the actual charge by entering =B2-D2.
  • Save the spreadsheet.

Before you compare the values, do you expect the calculated charge in E2 to match the reported final cost in C2?

  • Compare the value in E2 with the value in C2.

You'll see the two values match. This confirms that the reservation minus the refund equals the actual Job 1 charge.

Cost values do not match?

  • Check that B2 contains reserved_cost_usd from the start response.
  • Check that C2 contains final_cost_usd from the completed response.
  • Check that D2 contains refunded_usd as a number without a currency symbol.

Help me reconcile my Desearch cost log.

That is the billing trail locked down. Job 1 now has a completed response plus a cost log that proves its actual charge.

Next, you'll export the completed company results as a CSV that you can inspect in your spreadsheet application.

Export the First Results

Your first Desearch Company Search job now has a confirmed final cost. Next, you will turn that completed result into a list you can review.

A CSV exposes each returned company as one spreadsheet row. You will also separate the domains so a later search can skip them.

In this step, get ready to:
  • Export the completed Job 1 results as a CSV file.
  • Inspect the returned company details in your spreadsheet.
  • Save the Job 1 domains as an exclusion list.
Prepare the finished result

The export endpoint converts the completed task results into a spreadsheet-ready file. Exporting the results does not start another billed job.

  • Switch back to the terminal session where you polled Job 1.
  • Download the completed Job 1 results by running this command:
curl --request GET "https://api.desearch.ai/desearch/companies/tasks/$TASK_ID/export?format=csv" \
 --header "Authorization: $DESEARCH_API_KEY" \
 --output companies.csv

What does this command do?

  • The $TASK_ID value identifies the completed Job 1 task.
  • The format=csv parameter requests a spreadsheet-compatible export.
  • The --output companies.csv option saves the response as companies.csv.
  • Wait for the terminal prompt to return.

You should see the download finish before the prompt returns. The companies.csv file is now stored in the folder used by your terminal.

Can't find the CSV file?

Make sure you used the terminal session from the polling step. That session contains the TASK_ID value for Job 1.

Check that the command ends with companies.csv. This name is what you search for in your spreadsheet application.

Help me troubleshoot my Company Search CSV export.

Inspect the company records

The CSV flattens each company profile into columns. This layout makes the returned details easier to scan without reading the original task response.

  • Switch back to the spreadsheet application from earlier.
  • Use its file-opening control to search for companies.csv.
  • Choose companies.csv from the search results.

You should see one header row followed by one row for each company delivered by Job 1.

How is the export organised?

  • The domain column gives the company website host.
  • The name column gives the company name.
  • The ceo_name and other role columns hold returned leader details.
  • The ceo_email_status and other status columns show the outcomes of email checks.
  • Scan the name column for the returned company names.
  • Compare each company name with its domain value.
  • Move horizontally to the role-based leader columns.
  • Compare a populated leader email with its email status.

You can now review company identities directly in the spreadsheet. You can also see which leader emails received a check result.

Does the CSV look compressed?

If every value appears in one column, use your spreadsheet application's delimited import option. Choose a comma as the separator.

Help me separate the CSV into columns.

Build the domain exclusion list

A later search can pass these domains in the exclude parameter. Keeping a domain-only file makes that input easy to prepare.

  • Keep companies.csv open in your spreadsheet application.
  • Create a new blank spreadsheet document using the application's new-document control.

You should see a blank grid ready to hold the domain list.

  • Return to the open companies.csv document.
  • Select the populated cells below the domain header.
  • Copy the selected cells.

Your spreadsheet should highlight the copied domain cells.

  • Switch to the blank spreadsheet document.
  • Enter domain in the first cell.
  • Paste the copied values below the header.

You should see a single column containing the website domain for every exported company.

  • Save the domain-only document as job-1-domains.csv.

Before you check, do you expect the domain list to contain more entries or the same number of entries as the company export?

  • Compare the populated rows in job-1-domains.csv with the company rows in companies.csv.
  • Confirm that each company row has a corresponding domain entry.
  • Confirm that companies.csv still shows company name columns.
  • Confirm that companies.csv still shows leader columns.
  • Confirm that companies.csv still shows checked email status columns.

The domain list should contain one entry for each company row. Your original CSV should still contain the complete exported company details.

Good work. Your first paid result is now reviewable as a spreadsheet with a separate domain exclusion list.

Next, you will pause a new job before resuming the same work without a new charge.

Pause and Resume a Search Job

Job 1 is complete. Its results and final cost are safely recorded.

A background search needs a safe way to stop without losing its progress. Starting over would risk duplicating work and creating another reservation.

The Desearch Company Search API can pause an existing task. Its resume endpoint continues the same task without charging it again.

In this step, get ready to:
  • Start Job 2 with a capped company count.
  • Pause Job 2 through its pause endpoint.
  • Resume Job 2 without another charge.
Start Job 2 with a capped search

Starting Job 2 is the only action in this step that places a new reservation. The count value of 5 uses the smallest allowed company cap.

  • Switch back to the terminal from earlier.
  • Start the capped Company Search task by running:
curl --request POST 'https://api.desearch.ai/desearch/companies/search' \
  --header "Authorization: $DESEARCH_API_KEY" \
  --header 'Content-Type: application/json' \
  --data '{
  "query": "Series A fintech startups in Berlin building payment infrastructure",
  "count": 5
  }'

What does this request do?

  • The request starts a background Company Search task.
  • The query field describes the companies Job 2 should find.
  • The count field caps the task at five delivered companies.
  • The JSON response contains the task identifier and reserved cost.

You should receive a 202 Accepted response. Its status starts as queued.

  • Locate task_id in the start response.
  • Record the value here: your Job 2 task ID.
  • Locate reserved_cost_usd in the start response.
  • Record the value here: your Job 2 reserved cost.
  • Return to your cost log from earlier.
  • Add Job 2's task ID and reserved cost to the cost log.

Job 2 did not start?

Confirm that the terminal still has access to the API key you stored earlier. Check that the authorization header uses the environment variable shown in the request.

The account balance must cover the new reservation. Check your Desearch balance if authentication succeeds without creating a task.

Help me troubleshoot the Job 2 start request.

Pause Job 2 and confirm its state

A queued task pauses immediately. A running task finishes its current batch before its status moves from pausing to paused.

Companies delivered before the pause remain attached to Job 2. The original reservation also remains attached to the same task.

  • Pause Job 2 by running:
curl -X POST "https://api.desearch.ai/desearch/companies/tasks/[[JOB_2_TASK_ID="your Job 2 task ID"]]/pause" \
  -H "Authorization: $DESEARCH_API_KEY" \
  -H "Content-Type: application/json"

What does the pause request preserve?

The pause endpoint changes the state of the existing task. It keeps the same task_id and preserves the companies delivered so far.

The response may show paused immediately. A running batch can produce the temporary pausing status.

You should see Job 2's original task_id in the pause response. Its status should read paused or pausing.

  • Copy the pause response into a section named Pause in your Job 2 Pause and Resume Responses record.
  • Check Job 2's current state by running:
curl --request GET "https://api.desearch.ai/desearch/companies/tasks/[[JOB_2_TASK_ID="your Job 2 task ID"]]" \
  --header "Authorization: $DESEARCH_API_KEY"

What does the status request show?

This free request reads Job 2 without changing it. The response includes its current status and reservation details.

Repeating the request lets you watch a running task finish its current batch. The pause is complete when status reads paused.

  • Repeat the status request if status still reads pausing.
  • Copy the final paused response beneath the first pause response.
  • Record Job 2's paused status in your cost log.

Job 2 is now stopped without losing its task identity or delivered companies.

Job 2 is not reaching paused?

Check that the task identifier in the request matches Job 2. A Job 1 identifier points the request at the completed task from earlier.

A pausing status means the current batch is still finishing. Repeat the status request after a short wait.

Help me pause Job 2.

Resume Job 2 without another charge

Job 2 now has a saved task state and its original reservation. The resume endpoint continues that same task for free.

  • Continue the paused task by running:
curl -X POST "https://api.desearch.ai/desearch/companies/tasks/[[JOB_2_TASK_ID="your Job 2 task ID"]]/resume" \
  -H "Authorization: $DESEARCH_API_KEY" \
  -H "Content-Type: application/json"

What does the resume request do?

The resume endpoint continues Job 2 from the point where it stopped. Previously delivered companies stay attached to the same task.

Resuming is free. The call uses Job 2's existing reservation instead of creating another one.

You should see the original task_id in the resume response. Its status should have moved on from paused.

  • Copy the resume response into a section named Resume in your Job 2 Pause and Resume Responses record.
  • Record the returned resume status in your cost log.

Before you check, do you expect Job 2 to have a new task identifier or the original one?

  • Fetch Job 2's current state by running:
curl --request GET "https://api.desearch.ai/desearch/companies/tasks/[[JOB_2_TASK_ID="your Job 2 task ID"]]" \
  --header "Authorization: $DESEARCH_API_KEY"

What does this final check prove?

The status request gives you a fresh view of Job 2 after the resume action. You can compare its task identity with the original start response.

You can also compare reserved_cost_usd with the original reservation. An unchanged value confirms that resume did not create an additional reservation.

You should see Job 2's original task_id. The reserved_cost_usd value should still match the amount recorded when Job 2 started.

The status can read queued, running, or completed after the resume action.

  • Compare the returned task_id with your Job 2 task ID.
  • Compare the returned reserved_cost_usd with your Job 2 reserved cost.
  • Add a note to your cost log stating that Job 2 resumed without an additional reservation.

That control point is working. Job 2 can stop and continue while keeping its original reservation intact.

Resume request did not continue the job?

Confirm that Job 2 reached paused before you sent the resume request. The resume action only applies to a paused task.

Check that every Job 2 request uses the same task identifier. A different identifier prevents a valid reservation comparison.

Help me verify the pause and resume flow.

Job 2 can now pause and resume without duplicating its reservation. Next, you'll use the domains from Job 1 to build a follow-up list with no repeated companies.

Exclude Companies You Already Found

Your resumed Job 2 proves that a paused search can continue without another reservation. A wider search can still return companies that Job 1 already delivered.

The Desearch Company Search exclude array blocks those existing domains from the follow-up job. You will use that safeguard to produce a deduplicated CSV.

In this step, get ready to:
  • Run Job 3 with every Job 1 domain excluded.
  • Export Job 3 as a second CSV.
  • Produce one combined CSV with no repeated domains.
Start the excluded follow-up search

The exclude array accepts company domains or URLs that Desearch should leave out. Desearch reduces each entry to its host before checking candidates.

This is the only billed action in this step. The count value of 5 limits the reservation to five companies.

  • Switch back to the terminal from earlier.
  • Copy the plain-language company description from your Job 1 request into paste the exact Job 1 query.
  • Format every domain in the Job 1 domain list as a quoted JSON string.
  • Separate each formatted domain with a comma.
  • Save the formatted entries in paste Job 1 domains as quoted JSON strings separated by commas.
  • Start Job 3 with those exclusions by running:
curl --request POST 'https://api.desearch.ai/desearch/companies/search' \
 --header "Authorization: $DESEARCH_API_KEY" \
 --header 'Content-Type: application/json' \
 --header "Idempotency-Key: company-search-job-3" \
 --data '{
 "query": "[[JOB_1_QUERY="paste the exact Job 1 query"]]",
 "count": 5,
 "exclude": [
 [[JOB_1_DOMAINS="paste Job 1 domains as quoted JSON strings separated by commas"]]
 ]
 }'

What does this request do?

  • The query field reuses the company profile from Job 1.
  • The count field caps Job 3 at five delivered companies.
  • The exclude field removes every Job 1 domain from consideration.
  • The Idempotency-Key header gives this start request a stable identity.

You should receive a task response containing task_id, status, and reserved_cost_usd.

  • Copy the response's task_id into your Job 3 task ID.

Good. Job 3 now has its own identifier plus a fixed delivery cap.

Was the start request rejected?

  • Check that every exclude entry is a quoted domain.
  • Check that commas separate the domain entries.
  • Confirm that your stored API key remains available to the terminal.

Ask for help with the request structure: Help me debug my Desearch Company Search request with an exclude array.

Before you poll Job 3, do you expect the task to be finished already or still processing?

  • Check Job 3's current status by running:
curl --request GET "https://api.desearch.ai/desearch/companies/tasks/[[JOB_3_TASK_ID="your Job 3 task ID"]]" \
 --header "Authorization: $DESEARCH_API_KEY"

What does this status request show?

This free request reads Job 3 without starting another search. Its response shows the current status plus the latest progress.

Once the task ends, the same response includes final_cost_usd plus refunded_usd.

  • Wait a few seconds when the status remains queued or running.
  • Repeat the polling command.

When Job 3 finishes, you should see completed in the status field. The response should also show its final cost details.

Is Job 3 still processing?

  • Wait several more seconds before polling again.
  • Check the response for a status that shows the task has stopped.
  • Review any warning in the response before continuing.

Get help reading the task response: Help me understand why my Desearch Company Search task has not completed.

Export and compare the result sets

A completed export contains Job 3's latest company records. Its domain column provides the direct comparison point against Job 1.

  • Download Job 3 as job-3-companies.csv by running:
curl --request GET "https://api.desearch.ai/desearch/companies/tasks/[[JOB_3_TASK_ID="your Job 3 task ID"]]/export?format=csv" \
 --header "Authorization: $DESEARCH_API_KEY" \
 --output job-3-companies.csv

What does this export command do?

The export endpoint downloads every company that Job 3 delivered. The format=csv parameter requests spreadsheet-ready output.

The --output option saves that response locally as job-3-companies.csv.

  • Switch back to the spreadsheet application from earlier.
  • Use its file-opening control to load job-3-companies.csv from the terminal's current folder.

You should see one row per Job 3 company. The sheet should include a domain column plus company and contact details.

Is the Job 3 CSV empty?

  • Confirm that Job 3 reached an ended status before exporting.
  • Check that the task ID in the export request matches Job 3.
  • Download the export again after confirming the task response.

Get help checking the export: Help me troubleshoot an empty Desearch Company Search CSV export.

Before you compare the files, do you expect any Job 1 domain to appear in the Job 3 CSV?

  • Keep job-3-companies.csv active in the spreadsheet application.
  • Press Cmd+F on macOS or Ctrl+F on Windows to open the find control.
  • Paste the first domain from the Job 1 domain list into the find field.
  • Repeat the search for every remaining Job 1 domain.

Each search should find no matching cell in the Job 3 CSV. That result confirms the API skipped every excluded domain.

Did a Job 1 domain appear?

  • Compare the matching domain with the exact entries sent in exclude.
  • Check the start response to confirm you are comparing against Job 3.
  • Repeat Job 3 with the missing domain added to the exclusion list.

Ask for help tracing the overlap: Help me find why an excluded company domain appeared in Job 3.

Combine the CSVs and record the cost

The zero-overlap check makes the two exports safe to merge. Keeping one header row produces a clean combined file.

  • Switch back to the Job 1 Results CSV from earlier.
  • Save a copy as combined-companies.csv.

You should now see combined-companies.csv as the active file name. Its current rows are the original Job 1 results.

  • Switch to job-3-companies.csv.
  • Select every data row beneath the header row.

The selected range should contain only Job 3 companies. The CSV header should remain unselected.

  • Copy the selected Job 3 rows.
  • Switch to combined-companies.csv.

The combined file should still show every Job 1 row. Its first empty row is the insertion point for Job 3.

  • Select the first empty row below the Job 1 data.
  • Paste the copied Job 3 rows.

You should now see the Job 3 companies directly below the Job 1 companies. Only one header row should remain.

  • Save combined-companies.csv.
  • Return to the cost log from earlier.

The combined CSV now contains the non-duplicated companies from both completed searches.

  • Add Job 3's task_id to the cost log.
  • Add Job 3's reserved_cost_usd to the cost log.

Job 3 now has a traceable identity plus its original reservation in the cost log.

  • Add Job 3's final_cost_usd to the cost log.
  • Add Job 3's refunded_usd to the cost log.

The Job 3 record now shows the reservation plus the final cost details.

Before the final check, do you expect any domain to appear twice in the combined CSV?

  • Return to combined-companies.csv.
  • Compare its number of data rows with the total data rows from the two source CSVs.
  • Inspect the domain column for repeated values.

The combined row count should equal the Job 1 count plus the Job 3 count. Every company domain should appear once.

That is the key result: your follow-up search expanded the list without charging you for companies you had already found.

Your combined list now contains two completed searches with no repeated company domains. Next, you will resend the same start request and prove that its request key prevents a second charge.

Verify Idempotent Job Creation

Your first search and exclusion-based follow-up now form one combined list without repeated domains. A duplicated start call could undo that cost control by creating another paid job.

Idempotency makes a retried start request resolve to the first task. In this step, you'll prove that the Desearch Company Search API returns Job 1 without creating another job or charge.

In this step, get ready to:
  • Recover the original Job 1 request with its Idempotency-Key.
  • Resend the request without changing its inputs.
  • Confirm the response points to Job 1.
Find the original Job 1 request

The API links a retry to the original task through the exact header value. Keeping the original request unchanged gives you a clean comparison.

  • Switch back to the terminal from earlier.
  • Press the Up Arrow until the original Job 1 start request is visible.
  • Confirm the request targets https://api.desearch.ai/desearch/companies/search.
  • Confirm the Idempotency-Key header contains the exact value used for the original request.
  • Confirm the query value matches the original request.
  • Confirm the count value matches the original request.
  • Leave the request at the prompt without pressing Enter.

Why Does the Key Need to Match?

The API treats a new Idempotency-Key as a new task. Reusing the original value tells Desearch that this call is a retry of Job 1.

The repeated key protects your budget when a network problem leaves you unsure whether the first request succeeded.

Resend the unchanged start request

The repeated key is the control for this test. The returned task identity is the result you will compare.

Before you resend the request, do you expect Desearch to return a new task or Job 1?

  • Press Enter to resend the visible request.

You'll receive a response containing Job 1's original task_id. The response also shows the original reserved_cost_usd value.

Desearch handles this duplicate request with HTTP 200. The repeated call is not charged again.

  • Copy the returned response.
  • Save the response alongside the existing Job 1 start response as your idempotency verification.
  • Compare the returned task_id with the Job 1 task ID in your cost log.

What Does the Response Prove?

  • The matching task_id shows that the API returned the existing job.
  • The returned reserved_cost_usd belongs to Job 1's original reservation.
  • The repeated Idempotency-Key prevents a second job from being billed.

Did the Task ID Change?

  • Stop before sending another retry.
  • Compare the header value with the original Idempotency-Key.
  • Confirm the request still targets https://api.desearch.ai/desearch/companies/search.
  • Compare the request body with the original Job 1 body.

Help me understand why my repeated Desearch request returned a different task ID.

Confirm there is no duplicate job

The matching API response proves that both calls resolve to the same task. The dashboard gives you a second visible check because a successful retry adds no new job.

Before you refresh the dashboard, do you expect a new task to appear at the retry time?

  • Return to the signed-in Desearch dashboard from earlier.
  • Refresh the job list.
  • Check for a task created at the retry time.

You'll see no new task at the retry time. Job 1 keeps its original identity.

  • Switch back to the cost log from earlier.
  • Add an idempotency entry containing the original task_id, the returned task_id, the returned reserved_cost_usd, plus confirmation that no duplicate job or charge appeared.
  • Save the cost log.

Your cost log now records matching task IDs. It also records that the retry created no additional job or charge.

That's the final safety check done. Your retry now recovers Job 1 without multiplying jobs or spend.

Secret mission

Build a Unit-Cost Audit

Use your combined company list and cost log to calculate the cost of each unique company you collected. You will verify the calculation without starting another search or changing your existing project files.

Clean Up Your Resources

Clean Up Your Resources

Resumed Job 2 in Desearch Company Search can continue using its capped budget while your local CSV exports create no ongoing cost. Decide whether to keep the job running, pause it for later, or stop the work and remove your API access.

Cost warning

Company Search charges for delivered companies. Job 2 may continue delivering companies until it reaches its count cap.

Both the Pause and Delete options include pausing Job 2.

Resources you used:

  • Resumed Job 2 in Desearch Company Search.
  • Desearch API Key used for authenticated requests.
  • Combined Results CSV and Cost Log.
  • Supporting local records, including Job 1 Results CSV, Job 3 Results CSV, Job 1 Domains, saved job responses, and Idempotency Verification.

Keep everything running

No action needed. Choose this if you want Job 2 to finish or plan to run more searches.

  • Let resumed Job 2 finish within its count cap.
  • Keep the Desearch API Key in secure storage.
  • Retain Combined Results CSV as your deduplicated company list.
  • Retain Cost Log as your record of reservations, refunds, final charges, and idempotency protection.

Pause - I'll come back to this later

This option stops ongoing search work while keeping your files and account access ready for later.

  • Inspect Job 2's current state by repeating the polling request from earlier.
  • Repeat the authenticated pause request from earlier if Job 2 is still running.
  • Confirm the latest task response shows Job 2 is paused or complete.
  • Keep the Desearch API Key and local project artifacts securely stored.

Delete - I don't want to use this again

Deleting the Desearch API Key permanently blocks future requests made with that key. Your completed CSV data stays available until you remove the local copies below.

  • Inspect Job 2's current state by repeating the polling request from earlier.
  • Repeat the authenticated pause request from earlier if Job 2 is still running.
  • Confirm the latest task response shows Job 2 is paused or complete.
  • Delete the Desearch API Key from the same dashboard area where you created it.
  • Confirm the dashboard no longer shows the deleted key as available.
  • Use Finder on macOS or File Explorer on Windows to locate the local project artifacts you saved during the steps.
  • Move those local project artifacts to the Trash on macOS or the Recycle Bin on Windows.
  • Empty the Trash on macOS or the Recycle Bin on Windows to remove the local copies permanently.

Nice Work!

Nice Work!

You did it! You built a budget-controlled company discovery workflow with the Desearch Company Search API. Your combined CSV contains companies with leaders and checked emails. It also stays free of duplicate companies across Job 1 and Job 3.

What you accomplished:

  • Applied a company-count cap to control the maximum cost of each search. Recorded each job's complete cost history in a cost log.
  • Managed job safety by pausing a running search. Resumed the same job without a second charge. Reused an Idempotency-Key to retrieve the original job without creating another charge.
  • Created a deduplicated company list by exporting Job 1. Supplied its domains through exclude for Job 3. Confirmed that the combined CSV contains no repeated companies.
  • Secret Mission: Completed an optional challenge to push your budget-controlled company search skills further.

Ready to quiz yourself?