Uploading with multipart/form-data Payloads

Upload endpoints require a payload of type multipart/form-data as defined by RFC 7578. Most HTTP clients provide features that simplify this process.

You can use any library or platform that can send HTTP requests. This page offers code samples for some popular technologies, including:


You can upload a file to Ocrolus using cURL with its--form option. Use the @ syntax to refer to a particular file on your file system.

curl \
  --url "https://api.ocrolus.com/v1/book/upload?book_uuid=$THE_BOOK_UUID" \
  --oauth2-bearer "eyJhb...ciOSiU" \
  --form upload="@./path/to/document.pdf"
curl \
  --user 'api_key:api_secret' \
  --form 'pk=123456' \
  --form '[email protected]/path/to/document.pdf' \


Which operating system?

Most shells will handle the preceding syntax identically. The most notable exceptions are PowerShell and Windows CMD. They respectively use backticks (`) and carets (^) instead of backslashes (\) for line continuations.


You can use the requests library and pass the files to requests.post's files keyword parameter, as demonstrated in the following code sample.

# You'll need to install the requests and requests-oauthlib libraries
from oauthlib.oauth2 import BackendApplicationClient
from requests_oauthlib import OAuth2Session

client_id = "your_client_id"
client_secret = "your_client_secret"
# Get these from the Ocrolus Dashboard (and don't share them with anyone)

client = BackendApplicationClient(client_id=client_id)
session = OAuth2Session(client=client)
token = session.fetch_token("https://auth.ocrolus.com/oauth/token", client_secret=client_secret)

filepath = "/path/to/the/file.pdf"
url = "https://api.ocrolus.com/v1/book/upload"

data = {"pk": "the_book_pk"}  # The book's primary key, as a string or an int

with open(filepath, "rb") as file:
    files = {"upload": file}
    response = session.post(url, data=data, files=files)
import requests

filepath = '/path/to/bank-statement.pdf'
url = "https://api.ocrolus.com/v1/book/upload"

auth = ('api_key', 'api_secret')
data = {'pk': '123456'} # The book's primary key
files = { 'upload': open(filepath,'rb') }

response = requests.post(url, auth=auth, data=data, files=files)

The library will automatically apply the correct Content-Type and Authorization headers when used as shown. Remember to close the opened file (possibly using a with statement) once you're done with it!


Axios is a popular option that pairs well with the form-data package, as demonstrated below:

const fs = require('fs'),
           axios = require('axios'),
        FormData = require('form-data');

const form_data = new FormData();
form_data.append('pk', '123456');
form_data.append('upload', fs.createReadStream('./index.js'));

const ocrolusApi = axios.create({
  baseURL: 'https://api.ocrolus.com/v1/',
  auth: { username: 'api-key', password: 'api-secret' }

  '/book/upload', form_data, { headers: form_data.getHeaders() }
).then(res => {
  console.log('response', res.data);
}).catch(err => {
  console.log('error', err);


Prefer something else?

The preceding code sample is written in JavaScript, but you can use the demonstrated APIs with any language that compiles to it (e.g. CoffeeScript or TypeScript).


You can encode multipart/form-data payloads with APIs offered by Spring or Java EE. You could use Spring's MultipartBodyBuilder or Java EE's MimeMultipart classes.

import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.core.io.FileSystemResource;
import org.springframework.http.MediaType;
import org.springframework.http.client.MultipartBodyBuilder;
import org.springframework.web.reactive.function.client.WebClient;

public class Main {
  public static void main(String[] args) {
    WebClient client = WebClient.builder().
        defaultHeaders(h -> {
          h.setBasicAuth("api-key", "api-secret");

    MultipartBodyBuilder bookUploadBuilder = new MultipartBodyBuilder();
    bookUploadBuilder.part("pk", 123456);
    bookUploadBuilder.part("upload", new FileSystemResource("path/to/bank-statement.pdf"));

import java.net.URI;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import org.springframework.core.io.FileSystemResource;
import org.springframework.http.MediaType;
import org.springframework.http.RequestEntity;
import org.springframework.http.client.MultipartBodyBuilder;
import org.springframework.web.client.RestTemplate;

public class Main {
  public static void main(String[] args) {
    RestTemplate client = new RestTemplate();

    MultipartBodyBuilder bookUploadBuilder = new MultipartBodyBuilder();
    bookUploadBuilder.part("pk", 123456);
    bookUploadBuilder.part("upload", new FileSystemResource("./Main.java"));

    RequestEntity bookUploadRequest = RequestEntity.
        headers(h -> h.setBasicAuth("api-key", "api-secret")).

    System.out.println(client.exchange(bookUploadRequest, String.class).getBody());
import java.io.BufferedReader;
import java.io.File;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.IOException;
import java.io.UnsupportedEncodingException;

import java.net.URL;
import java.net.HttpURLConnection;

import java.util.Base64;

import java.util.stream.Collectors;

import javax.mail.MessagingException;
import javax.mail.internet.MimeBodyPart;
import javax.mail.internet.MimeMultipart;

class Main {
  public static void main(String[] args) throws IOException, MessagingException {
    MimeMultipart multipart = makePayload("123456", "path/to/bank-statement.pdf");
    URL url = new URL("https://api.ocrolus.com/v1/book/upload");
    HttpURLConnection connection = (HttpURLConnection)url.openConnection();

    connection.setRequestProperty("Authorization", makeAuthorizationHeader("api-key", "api-secret"));
    connection.setRequestProperty("Content-Type", multipart.getContentType().replaceAll("(?ms)\\s*\r\n\\s*", " "));


    System.out.println(String.format("[%d] %s", connection.getResponseCode(), connection.getResponseMessage()));

    BufferedReader reader = new BufferedReader(new InputStreamReader((InputStream)connection.getContent()));

  private static String makeAuthorizationHeader(String username, String password) throws UnsupportedEncodingException {
    byte[] byteData = String.format("%s:%s", username, password).getBytes();
    return String.format("Basic %s", Base64.getMimeEncoder().encodeToString(byteData));

  private static MimeMultipart makePayload(String bookPK, String filePath) throws IOException, MessagingException {
    MimeBodyPart bookPKPart = new MimeBodyPart();
    bookPKPart.setDisposition("form-data; name=pk");
    bookPKPart.addHeader("Content-Type", "text/plain; charset=utf-8");

    MimeBodyPart filePart = new MimeBodyPart();
    filePart.attachFile(new File(filePath));
    filePart.setDisposition("form-data; name=upload");

    MimeMultipart multipart = new MimeMultipart();

    return multipart;


Prefer something else?

The preceding code samples are written in Java, but the demonstrated APIs are usable with any language that runs on the JVM (e.g. Kotlin or Scala).


You can use .NET's HttpClient and MultipartFormDataContent classes to encode your request.

using System;
using System.IO;
using System.Text;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Threading.Tasks;

class MainClass {
  public static string BOOK_PK = "123456";

  public static void Main (string[] args) {
    HttpClient client = new HttpClient() { BaseAddress = new Uri("https://api.ocrolus.com/v1/") };
    client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue(
      "Basic", MakeAuthorizationHeaderValue("api_key", "api_secret"));

    WriteResponse(client.PostAsync("book/upload", MakePayload(BOOK_PK, "./path/to/bank-statement.pdf")));

  private static string MakeAuthorizationHeaderValue(string username, string password) {
    return Convert.ToBase64String(ASCIIEncoding.ASCII.GetBytes($"{username}:{password}"));
  private static HttpContent MakePayload(string bookPk, string filePath) {
    MultipartFormDataContent content = new MultipartFormDataContent();

    // Add Book PK to payload
    content.Add(new StringContent(bookPk), "pk");

    // Add File content to payload
    FileInfo file = new FileInfo(filePath);
    content.Add(new StreamContent(file.OpenRead()), "upload", file.Name);
    // Debug payload
    // content.CopyToAsync(Console.OpenStandardOutput()).Wait();

    return content;

  private static void WriteResponse(Task<HttpResponseMessage> responseTask) {


Prefer something else?

The preceding code sample is written in C#, but the demonstrated APIs are usable with any language that uses the .NET runtime (e.g. F# or Visual Basic).


You can use Go's standard http and multipart packages to encode your request.

package main

import (

func main() {
    req, err := newUploadRequest(123456, "path/to/bank-statement.pdf")
    if err != nil {
    req.SetBasicAuth("api-key", "api-secret")

    client := &http.Client{}

    res, err := client.Do(req)
    if err != nil {
    defer res.Body.Close()

    _, err = io.Copy(os.Stdout, res.Body)
    if err != nil {

func newUploadRequest(bookPK int64, filePath string) (*http.Request, error) {
    body, contentType, err := newUploadBody(bookPK, filePath)
    if err != nil {
        return nil, err

    req, err := http.NewRequest(http.MethodPost, "https://api.ocrolus.com/v1/book/upload", body)
    if err != nil {
        return nil, err
    req.Header.Set("Content-Type", contentType)

    return req, nil

func newUploadBody(bookPK int64, filePath string) (io.Reader, string, error) {
    bodyReader, bodyWriter := io.Pipe()
    uploadWriter := multipart.NewWriter(bodyWriter)

    file, err := os.Open(filePath)
    if err != nil {
        return nil, "", err

    go func() {
        defer bodyWriter.Close()
        defer file.Close()

        err = uploadWriter.WriteField("pk", strconv.FormatInt(bookPK, 10))
        if err != nil {

        part, err := uploadWriter.CreateFormFile("upload", file.Name())
        if err != nil {

        _, err = io.Copy(part, file)
        if err != nil {

        err = uploadWriter.Close()
        if err != nil {

    return bodyReader, uploadWriter.FormDataContentType(), nil


You can use PHP's cURL extension with the CURLFile class to encode your request.


$username = "username";
$password = "password";
$pk = '123456';
$filepath = "path/to/pdf";
$filename = "bank-statement.pdf";

$request = curl_init('https://api.ocrolus.com/v1/book/upload');
$postdata = array(
  'pk' => $pk,
  'upload' => curl_file_create(
    $filepath . '/' . $filename,

curl_setopt($request, CURLOPT_USERPWD, $username . ":" . $password);
curl_setopt($request, CURLOPT_POST, true);
curl_setopt($request, CURLOPT_POSTFIELDS, $postdata);

curl_setopt($request, CURLOPT_RETURNTRANSFER, true);
echo curl_exec($request);