curl --request POST \
--url https://api.baselayer.com/lien_submissions/import/batches \
--header 'Content-Type: multipart/form-data' \
--header 'X-API-Key: <api-key>' \
--form file='@example-file'import requests
url = "https://api.baselayer.com/lien_submissions/import/batches"
files = { "file": ("example-file", open("example-file", "rb")) }
headers = {"X-API-Key": "<api-key>"}
response = requests.post(url, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('file', '<string>');
const options = {method: 'POST', headers: {'X-API-Key': '<api-key>'}};
options.body = form;
fetch('https://api.baselayer.com/lien_submissions/import/batches', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.baselayer.com/lien_submissions/import/batches",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Content-Type: multipart/form-data",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.baselayer.com/lien_submissions/import/batches"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.baselayer.com/lien_submissions/import/batches")
.header("X-API-Key", "<api-key>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.baselayer.com/lien_submissions/import/batches")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "q3-portfolio-liens.csv",
"state": "PENDING",
"total_count": 100,
"created_at": "2023-11-07T05:31:56Z",
"completed_count": 0,
"progress": 0,
"imported_count": 80,
"already_imported_count": 15,
"not_found_count": 4,
"invalid_row_count": 1,
"error": "The uploaded CSV could not be read from storage."
}{
"code": 602,
"message": "Invalid file type. Please upload a CSV file.",
"metadata": {}
}{
"code": 500,
"message": "Payment Required. Sandbox applications do not support this request.",
"metadata": {}
}{
"code": 900,
"message": "The file is too large. Please upload a smaller file.",
"metadata": {}
}{
"code": 801,
"message": "A bulk lien import exceeds the maximum allowed number of rows.",
"metadata": {}
}{
"code": 7,
"message": "The request could not be completed due to a failure in an external service.",
"metadata": {}
}Import Lien Filings In Bulk
Submit a CSV of liens to import from the public record (one lien per row). Always processed asynchronously (202 + batch id); poll GET /lien_submissions/import/batches/ and download per-row results via /lien_submissions/import/batches//download. Batches are capped at 40,000 rows. Columns: state, filing_number, filing_name (required: state, filing_number). Only the file as a whole is validated on upload — headers, encoding, size and row count; a row the parser cannot map is reported per-row in the results instead. Rows are identified by their ORIGINAL line number in the uploaded file (the header is line 1, and skipped blank lines still take up their line). One row per line: a quoted field may not contain a line break, and a file with one is rejected naming the line the row starts on.
curl --request POST \
--url https://api.baselayer.com/lien_submissions/import/batches \
--header 'Content-Type: multipart/form-data' \
--header 'X-API-Key: <api-key>' \
--form file='@example-file'import requests
url = "https://api.baselayer.com/lien_submissions/import/batches"
files = { "file": ("example-file", open("example-file", "rb")) }
headers = {"X-API-Key": "<api-key>"}
response = requests.post(url, files=files, headers=headers)
print(response.text)const form = new FormData();
form.append('file', '<string>');
const options = {method: 'POST', headers: {'X-API-Key': '<api-key>'}};
options.body = form;
fetch('https://api.baselayer.com/lien_submissions/import/batches', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://api.baselayer.com/lien_submissions/import/batches",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--",
CURLOPT_HTTPHEADER => [
"Content-Type: multipart/form-data",
"X-API-Key: <api-key>"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://api.baselayer.com/lien_submissions/import/batches"
payload := strings.NewReader("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("X-API-Key", "<api-key>")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://api.baselayer.com/lien_submissions/import/batches")
.header("X-API-Key", "<api-key>")
.body("-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--")
.asString();require 'uri'
require 'net/http'
url = URI("https://api.baselayer.com/lien_submissions/import/batches")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["X-API-Key"] = '<api-key>'
request.body = "-----011000010111000001101001\r\nContent-Disposition: form-data; name=\"file\"; filename=\"example-file\"\r\nContent-Type: application/octet-stream\r\n\r\n<string>\r\n-----011000010111000001101001--"
response = http.request(request)
puts response.read_body{
"id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"name": "q3-portfolio-liens.csv",
"state": "PENDING",
"total_count": 100,
"created_at": "2023-11-07T05:31:56Z",
"completed_count": 0,
"progress": 0,
"imported_count": 80,
"already_imported_count": 15,
"not_found_count": 4,
"invalid_row_count": 1,
"error": "The uploaded CSV could not be read from storage."
}{
"code": 602,
"message": "Invalid file type. Please upload a CSV file.",
"metadata": {}
}{
"code": 500,
"message": "Payment Required. Sandbox applications do not support this request.",
"metadata": {}
}{
"code": 900,
"message": "The file is too large. Please upload a smaller file.",
"metadata": {}
}{
"code": 801,
"message": "A bulk lien import exceeds the maximum allowed number of rows.",
"metadata": {}
}{
"code": 7,
"message": "The request could not be completed due to a failure in an external service.",
"metadata": {}
}Authorizations
Body
Response
The import batch was accepted and is being processed.
A bulk lien import (ENG-6884) — returned when one is accepted, by the polling endpoint, and as the payload of its completion/failure webhooks.
The four outcome counts partition the rows processed so far: every completed row is exactly one of imported / already imported / not found / invalid.
The batch's id, used to poll status and fetch per-row results.
The name of the batch, taken from the uploaded file.
"q3-portfolio-liens.csv"
Processing state of the batch.
PENDING, EXECUTING, COMPLETED, FAILED, CANCELLED "PENDING"
Rows read from the uploaded CSV, excluding blank lines.
100
When the batch was accepted.
Rows processed so far, whatever their outcome — the sum of the four outcome counts.
0
50
Fraction of the uploaded rows processed so far, in [0, 1].
0
0.5
1
Rows that resolved against the public record and produced a newly imported filing.
80
Rows naming a lien the organization had already imported; the existing filing is kept.
15
Rows whose state and filing number matched nothing in the public record.
4
Rows rejected before resolution (unparseable state, blank filing number, and the like).
1
Why the batch last failed, as a human-readable message. Set when the state is FAILED; a batch that failed once and succeeded on a resume can still carry the message from that attempt, so read it alongside the state rather than as a failure signal on its own.
"The uploaded CSV could not be read from storage."