Find businesses that can do a job

Start here. Describe the job once: zip, optional service_key, the home in property (kind of home, rooms, size, floor; see the word list), price inputs such as hours in inputs, and words from the word list in must_have, nice_to_have and must_not. The answer reads the job back in job.read_back. Returns matches (each with one exact price, a quote_token and bookable windows), needs_more (businesses that take the job but whose price needs something the job did not state, each with a needs list and a plain why) and near_misses (each reason with a plain code and a label, e.g. home_kind_not_taken "Takes apartments and condos only."). A business is never assumed to do something it has not answered, and is never sent a job it does not take (kind of home, largest home, a condition it marked not taken). Job conditions (pets, parking, mould, after building work and so on) go in inputs.conditions and are priced in, sent to the owner, or make the business a near-miss (condition_not_taken). Anything not on the word list goes in special_requests and always needs the owner's approval. Unknown words are refused with the word probably meant. Sending only service_key and zip returns the old merchants list. A search result is not a promise: prices and windows are as of availability_checked_at, and only a booked job is committed. A price from search can be held directly (it becomes a record when held); call quote to get a transaction_id before holding. Every result carries trust: the business's facts (who vouches for each: CommonTape checked, or the owner says so; since when and until when), facts that have lapsed, and its job record on CommonTape. Trust never changes the order, the price, who is eligible or what needs approval.

post/api/v1/search

Request

  • Base URL: https://commontape.com
  • URL: https://commontape.com/api/v1/search
  • Auth: HTTP bearer

Request body

zipstring required

The customer's ZIP.

service_keystring

Omit to consider every service.

must_havestring[]

Words the business must offer, e.g. task.fridge_interior, pay.card. A task at extra cost is priced in; a task marked ask first makes the match conditional.

nice_to_havestring[]

Words that rank a business higher but never exclude it.

must_notstring[]

Tasks to leave out, a way to pay the customer cannot use, or crew.solo.

special_requestsstring[]

Plain-text requests not on the word list. Never assumed: always ask first.

customer_refstring

Optional. Your own label for your customer. Two holds of the same job at one business are refused (HOLD_EXISTS) unless their customer_ref differs, so send one when you serve several customers. Never shown to the owner.

max_totalnumber

Optional. The most the customer will pay in all, in dollars (travel and extra charges included). A priced business above it is a near miss over_max_total with the gap in dollars; a window whose own day or hour charge would take the price over it is left out; a business with no price yet (needs_more, owner_must_quote) is listed with max_total not_checked in statuses.

arrive_bystring

Optional. The latest start: a date and time like 2026-10-09T10:00 (wall clock in the job ZIP's time zone, the business's zone) or with an offset (2026-10-09T14:00Z). Only windows that start by then are offered; none left is a near miss no_window_fits. Refused: a time that does not exist (clocks going forward), the past, more than 60 days ahead.

finish_bystring

Optional. The latest finish, written like arrive_by: a window is offered only if it starts early enough to end by then (window start plus job_hours, else the window's end). After arrive_by when both are sent.

id_to_security_hours_beforeinteger

Optional, with cond.id_to_security in inputs.conditions. How many hours before the job the building wants each cleaner's name and photo ID (1 to 168). A business that can send them at least this far ahead meets it; one that cannot is a near miss id_notice_too_short; one that has not said is not_said in statuses.

Example request

{
  "service_key": "house_cleaning.standard",
  "property": {
    "spaces": {
      "space.bedroom": 2,
      "space.bathroom.full": 2,
      "space.bathroom.half": 1,
      "space.kitchen": 1
    }
  },
  "scope": {
    "leave_out": [
      "space.garage"
    ],
    "clean_only": [
      "space.kitchen",
      "space.bathroom.full"
    ]
  },
  "max_total": 300,
  "arrive_by": "2026-10-09T10:00",
  "finish_by": "2026-10-09T15:00",
  "id_to_security_hours_before": 2
}

Response

Success

orderstring

The rule matches are ordered by. Trust never changes it.

order_keysstring[]

The same rule as keys, in order: full matches before conditional ones (never outranked on price), then businesses whose owner confirmed their facts in the last 7 days before stale ones (stale), then most nice-to-haves met, then lowest price, then a rotation for equal results.

taxonomy_versioninteger

The word list version this was made under (GET /api/v1/taxonomy/house_cleaning returns the current one). A job made before reads as 4.

availability_checked_atstring date-time

When this answer was made (the open windows were read then). Each business's own owner confirmation is its facts_confirmed_at.

taxonomystring
merchantsobject[]

Old request only.

Example response

{
  "taxonomy_version": 6,
  "job": {
    "property": {
      "spaces": {
        "space.bedroom": 2,
        "space.bathroom.full": 2,
        "space.bathroom.half": 1,
        "space.kitchen": 1
      }
    }
  },
  "matches": [
    {
      "scope": {
        "leave_out": [
          "space.garage"
        ],
        "clean_only": [
          "space.kitchen",
          "space.bathroom.full"
        ]
      },
      "booking_authority": {
        "reasons": [
          "owner_approves_every_job"
        ]
      }
    }
  ],
  "needs_more": [
    {
      "scope": {
        "leave_out": [
          "space.garage"
        ],
        "clean_only": [
          "space.kitchen",
          "space.bathroom.full"
        ]
      },
      "needs": [
        {
          "path": "property.size.square_feet"
        }
      ]
    }
  ],
  "owner_must_quote": [
    {
      "scope": {
        "leave_out": [
          "space.garage"
        ],
        "clean_only": [
          "space.kitchen",
          "space.bathroom.full"
        ]
      }
    }
  ],
  "near_misses": [
    {
      "scope": {
        "leave_out": [
          "space.garage"
        ],
        "clean_only": [
          "space.kitchen",
          "space.bathroom.full"
        ]
      }
    }
  ]
}

Changes

No recorded changes to this operation across all 1 revision of this API.