# UK Conveyancing API - Cases, Quotes, Search Orders (`nabeelbaghoor/uk-conveyancing-case-api`) Actor

Read a UK conveyancing and property search account into a dataset: quotes with every conveyancer fee quoted, cases with their people and documents, workflow tasks and completion, activity trails, notes, and compliance and search orders with their results. Read only: it never orders or uploads.

- **URL**: https://apify.com/nabeelbaghoor/uk-conveyancing-case-api.md
- **Developed by:** [Nabeel Hassan](https://apify.com/nabeelbaghoor) (community)
- **Categories:** Real estate, Business, Developer tools
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $12.00 / 1,000 case returneds

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

## UK Conveyancing API - Cases, Quotes, Search Orders

Pull your UK conveyancing and property search account into a dataset: every quote raised with every conveyancer fee quoted against it, every case those quotes became, the people and firms party to each one, the workflow tasks and how far through they are, the activity trail, the notes, and the compliance and property search orders placed alongside them with their results and documents.

### What this actor does

- **List cases and quotes.** Paged, filtered by status and by created or updated date, sorted however you like. The updated-since filter is the one to run a daily sync on.
- **Case details, expanded.** One row for the case, then a row per person with their roles, a row per document, plus the conveyancer acting, the accounts and contacts party to it, related cases, and the fee.
- **Quotes with every fee quoted.** A quote exists to compare conveyancers, so each conveyancer offered becomes its own row carrying its own fee and breakdown, with the selected one flagged.
- **Workflow tasks, flattened but not flattened out.** The task tree comes back as rows that keep their depth, ordinal and parent task, so the workflow order survives a sort and a subtask is never mistaken for a milestone.
- **Activity trails and notes.** Every activity with its type, message, metadata, author and timestamp. Every note with what it superseded, so the record reads as a history rather than a list.
- **Search and compliance orders.** Order status for polling, or the full order with its results exactly as the provider returned them.
- **Documents into the key value store.** Case and order attachments downloaded whole, with their media type preserved, and the store key written onto the matching row.
- **Pay per result.** You are charged for rows carrying a real record. Identifiers your account is not party to are free.

### Read only, by construction

The API behind this actor can create quotes, instruct conveyancers, place orders for paid property searches, upload documents and change case status. **None of that is reachable from here.** Every route this actor calls is a GET.

That is a deliberate boundary rather than an unfinished feature. A scheduled run against a live conveyancing account should not be able to spend money, instruct a firm, or move a case on, and the safest way to guarantee that is for the code to have no way of doing it.

### Bring your own key

This actor calls the provider with **your own client ID and client secret**, issued when your integration is onboarded. The client ID identifies the account, so there is no separate account header to set. They are exchanged once for an access token, which is cached until it is close to expiring exactly as the provider asks, and refreshed once automatically if a long run crosses an expiry.

Paste the secret into the input or set it as the `DATA_API_KEY` secret. Nothing runs on anyone else's credentials, and a run without them ends cleanly with an explanation rather than failing.

### Two environments, two sets of credentials

You are issued test credentials before live ones, and the two environments have **separate API hosts and separate identity hosts**. A test credential pointed at the live host is simply refused, with an error that says nothing about which host it went to.

So the environment switch in this actor moves both hosts together. Pick UAT while you are building and production when you go live, and the token endpoint follows.

### An empty 403 is not a permissions problem

Worth knowing before it happens. This gateway answers a path it does not route with **403 and an empty body**, while a path that exists but has no valid token answers **401 with a structured body** naming the problem. A permissions refusal on a real path answers 403 **with** a body.

Verified against the live host while building this actor. So an empty 403 means the request went somewhere the gateway does not serve, which is a bug in the actor rather than anything to do with your credentials, and this actor reports it that way rather than sending you to check a key that is fine.

### The fee breakdown, kept beside the total

A conveyancing quote's headline figure includes VAT and rolls three different things together: the legal work, the disbursements paid out to third parties, and any supplements for the particulars of the transaction. Two firms quoting the same total can be quoting very different amounts of legal work.

So the breakdown travels beside the total on every quote, conveyancer and case row, and nothing here derives one from the other.

### Example output

```json
{
  "recordType": "case",
  "requested": "cases",
  "found": true,
  "detailed": true,
  "caseId": "3fa85f64-5717-4562-b3fc-2c963f66afa6",
  "reference": "LMK-2026-004218",
  "status": "In Progress",
  "propertyAddress": "14 Bramley Road, Leeds, LS13 2QP",
  "productId": "sale-freehold",
  "propertyLocation": "EnglandWales",
  "conveyancerName": "Example Solicitors LLP",
  "conveyancerLocation": "Leeds",
  "createdDate": "2026-07-02T09:14:00Z",
  "lastUpdatedDate": "2026-09-11T16:20:00Z",
  "personCount": 2,
  "documentCount": 7,
  "feeTotalIncVat": 1284.5,
  "feeLegalWork": 795,
  "feeDisbursements": 389.5,
  "feeSupplements": 100
}
```

### FAQ

#### What does this actor read?

Conveyancing quotes and the conveyancers quoted on them, cases with their status, property, product, people, documents, contacts, accounts and fees, the workflow tasks on a case and their completion, the activity trail, the notes, and property search and compliance orders with their status, results and documents.

#### Can this actor place an order or instruct a conveyancer?

No. Every route it calls is a GET. It cannot create a quote, instruct a conveyancer, place an order, change a case status or upload a document, which is what makes it safe to schedule against a live account.

#### Why can I only see some of the cases?

Both sides of a transaction share one case record, and each account sees only the cases it is party to. An identifier that belongs to somebody else's case is reported as not found rather than as an error, because from your account's point of view those are the same thing.

#### How do I run a daily sync?

List cases with the updated-since filter set to yesterday, sorted by updated date. That returns everything that moved, and the case ID on each row is what the detail, task, activity and note modes take next.

#### What is the difference between a quote and a case?

A quote becomes a case once it is instructed. Before that it is a comparison: several conveyancers, each with their own fee. Afterwards it is a live matter with tasks, activities, notes and documents. This actor reads both, and the quote and case identifiers are different, so retrieve an instructed transaction through the case modes rather than the quote ones.

#### Are task status and completion the same thing?

No, and this API models them as two separate fields. A task carries a status and, separately, a completion flag and a completed date, and they can disagree. Both travel on every task row, along with the depth, ordinal and parent task that place it in the workflow.

#### Where do downloaded documents go?

Into the key value store of the run that fetched them, under a key built from the case or order and the document identifier, with the media type the provider sent preserved. The same key is written onto the document's row in the dataset, so the row and the file join up. A document the provider will not release is recorded with the reason rather than ending the run.

#### Which environment should I point this at?

UAT while you are building, production once your integration is live. They have separate credentials and separate hosts, and a test credential will not work against production, so the switch moves both the API host and the identity host together.

### Keyword map

UK conveyancing API, conveyancing case management API, property search order API, conveyancing quote API, conveyancer fee comparison, AML check API UK, anti money laundering property, source of funds check API, land registry ownership check, property compliance search API, conveyancing workflow tasks, case activity trail API, conveyancing documents API, UK property transaction data, panel conveyancing integration, proptech API UK, legal case data export, conveyancing pipeline reporting, OData case filter, Scottish conveyancing API

# Actor input Schema

## `mode` (type: `string`):

List cases and list quotes page through the account with filters and sorting. The details modes read one record and expand the people, conveyancer options, documents and fee behind it. Activities, tasks and notes read a case's history. The order modes read a compliance or property search order and its results. The download modes fetch attached documents into this run's key value store. Every mode reads: none of them creates, instructs, orders or uploads anything.

## `environment` (type: `string`):

Which environment to read. Test and live credentials are separate and are issued in that order, so this switch moves both the API host and the identity host together rather than leaving half of it behind.

## `caseIds` (type: `array`):

One case ID per line, for the case detail, activity, task, note and document modes. Run list cases first to find them.

## `quoteIds` (type: `array`):

One quote ID per line, for the quote detail mode. Run list quotes first to find them.

## `orderIds` (type: `array`):

One order ID per line, as returned when the order was placed, for the order modes.

## `status` (type: `string`):

Return only cases or quotes at this status. Leave empty for all of them.

## `createdFrom` (type: `string`):

Only return cases or quotes created on or after this date, as YYYY-MM-DD.

## `createdTo` (type: `string`):

Only return cases or quotes created on or before this date, as YYYY-MM-DD.

## `updatedFrom` (type: `string`):

Only return cases or quotes updated on or after this date, as YYYY-MM-DD. This is the filter to run a daily sync on.

## `filter` (type: `string`):

An extra OData filter clause, combined with the fields above using and. Written in the provider's own filter syntax, for example status ne 'Cancelled'.

## `orderBy` (type: `string`):

How to sort the listing, in the provider's own syntax, for example updatedDate desc or createdDate asc. Leave empty for the provider default, which is most recently updated first.

## `includePeople` (type: `boolean`):

Emit the people on each case or quote as their own rows, with their roles, contact details and correspondence address. One person is often both buyer and borrower, so roles arrive as a list.

## `includeDocuments` (type: `boolean`):

Emit the documents attached to each case or order as their own rows, with type, file name, media type and date. This lists them; the download modes fetch the files themselves.

## `includeOrderResults` (type: `boolean`):

Include the full results object on order rows. Results differ entirely by product, so they travel as the provider sent them rather than flattened onto columns that would fit only one product.

## `pageSize` (type: `integer`):

How many records to ask for per listing request. A run never asks for more rows than it is allowed to keep.

## `startAt` (type: `integer`):

Skip this many records before collecting. Use it to resume a large listing where a previous run stopped.

## `maxResults` (type: `integer`):

Stop after this many rows. Counts every row: cases, quotes, people, documents, activities, tasks and notes alike.

## `requestsPerMinute` (type: `integer`):

How fast to call the provider. Lower this if your account's rate limit is being hit.

## `clientId` (type: `string`):

Your own client ID, issued when your integration was onboarded. It identifies the account, so there is no separate account header. Stored encrypted and never written into the dataset.

## `clientSecret` (type: `string`):

Your own client secret. Exchanged once for an access token, which is cached until it is close to expiring rather than re-fetched per request. Can also be supplied as the DATA\_API\_KEY environment secret.

## `baseUrl` (type: `string`):

Overrides the API host. Leave empty unless the provider has given you a different one: the environment switch already moves it.

## `tokenUrl` (type: `string`):

Overrides the identity host the client ID and secret are exchanged at. Leave empty unless the provider has given you a different one.

## Actor input object example

```json
{
  "mode": "listCases",
  "environment": "production",
  "status": "",
  "includePeople": true,
  "includeDocuments": true,
  "includeOrderResults": true,
  "pageSize": 50,
  "startAt": 0,
  "maxResults": 100,
  "requestsPerMinute": 60
}
```

# Actor output Schema

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

One row per record, alongside the listing or identifier that produced it.

# 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 = {};

// Run the Actor and wait for it to finish
const run = await client.actor("nabeelbaghoor/uk-conveyancing-case-api").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 = {}

# Run the Actor and wait for it to finish
run = client.actor("nabeelbaghoor/uk-conveyancing-case-api").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 '{}' |
apify call nabeelbaghoor/uk-conveyancing-case-api --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "type": "http",
            "url": "https://mcp.apify.com/?tools=fetch-actor-details,nabeelbaghoor/uk-conveyancing-case-api"
        }
    }
}
```

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/rKeW5mP2OYZMLwRxZ/builds/uHgCvTu6a1HP8LTad/openapi.json
