NAV Navbar
cURL Python Android Swift Objective-C


git clone
cd api-examples/images

Welcome to the Mathpix API! You send us an image, we tell you about the math that’s in it. It’s that simple. We return latex as well as image metadata that you can use in your application. To see examples of images our engine can recognize, see the following link:

We have examples of how to call our API for Shell, Python, Android, and iOS. You can view code examples in the dark area to the right, and you can switch the programming language of the examples with the tabs in the top right.

Note for iOS developers: if you want to integrate Mathpix into your iOS app, there is no easier way than using the Mathpix SDK for iOS: With this SDK you can integrate Mathpix into your app with one single line of code without having to implement camera functionality. Also, the UI is fully customizable.

If you have any problems, please send us an email at


Processing a single image

Call the API as follows:

#!/usr/bin/env python
import sys
import base64
import requests
import json

# put desired file path here
file_path = 'limit.jpg'
image_uri = "data:image/jpg;base64," + base64.b64encode(open(file_path, "rb").read())
# (python3) image_uri = "data:image/jpg;base64," + base64.b64encode(open(file_path, "rb").read()).decode()
r ="",
    data=json.dumps({'src': image_uri}),
    headers={"app_id": "trial", "app_key": "34f1a4cea0eaca8540c95908b4dc84ab",
            "Content-type": "application/json"})
print json.dumps(json.loads(r.text), indent=4, sort_keys=True)
curl -X POST \
    -H 'app_id: trial' \
    -H 'app_key: 34f1a4cea0eaca8540c95908b4dc84ab' \
    -H 'Content-Type: application/json' \
    --data '{ "src": "data:image/jpeg;base64,'$(base64 -i limit.jpg)'" }'
// Using OKHttp
// git url :

OkHttpClient client = new OkHttpClient();

MediaType mediaType = MediaType.parse("application/json");
RequestBody body = RequestBody.create(mediaType, "{ \"src\" : \"data:image/jpeg;base64,{BASE64-STRING}\" }");
Request request = new Request.Builder()
  .addHeader("content-type", "application/json")
  .addHeader("app_id", "mathpix")
  .addHeader("app_key", "34f1a4cea0eaca8540c95908b4dc84ab")
Response response = client.newCall(request).execute();
Using NSUrl

import Foundation

let headers = [
  "content-type": "application/json",
  "app_id": "mathpix",
  "app_key": "34f1a4cea0eaca8540c95908b4dc84ab"
let parameters = ["src": "data:image/jpeg;base64,{BASE64-STRING}"] as [String : Any]

let postData = parameters, options: [])

let request = NSMutableURLRequest(url: NSURL(string: "")! as URL,
                                        cachePolicy: .useProtocolCachePolicy,
                                    timeoutInterval: 60.0)
request.httpMethod = "POST"
request.allHTTPHeaderFields = headers
request.httpBody = postData as Data

let session = URLSession.shared
let dataTask = session.dataTask(with: request as URLRequest, completionHandler: { (data, response, error) -> Void in
  if (error != nil) {
  } else {
    let httpResponse = response as? HTTPURLResponse


// Using Alamofire
// git url :

func processSingleImage(imageName : String) {
        if let data = NSData(contentsOfFile: imageName) {
            let base64String = data.base64EncodedString(options: .init(rawValue: 0))
            let parameters : Parameters = [
                "src" : "data:image/jpeg;base64," + base64String

                              method: .post,
                              parameters : parameters,
                              encoding: JSONEncoding.default,
                              headers: [
                                "app_id" : "mathpix",
                                "app_key" : "139ee4b61be2e4abcfb1238d9eb99902"
                .responseJSON{ response in
                if let JSON = response.result.value {
Using NSUrl

#import <Foundation/Foundation.h>

NSDictionary *headers = @{ @"content-type": @"application/json",
                           @"app_id": @"mathpix",
                           @"app_key": @"34f1a4cea0eaca8540c95908b4dc84ab"};
NSDictionary *parameters = @{ @"src": @"data:image/jpeg;base64,{BASE64-STRING}" };

NSData *postData = [NSJSONSerialization dataWithJSONObject:parameters options:0 error:nil];

NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@""]
[request setHTTPMethod:@"POST"];
[request setAllHTTPHeaderFields:headers];
[request setHTTPBody:postData];

NSURLSession *session = [NSURLSession sharedSession];
NSURLSessionDataTask *dataTask = [session dataTaskWithRequest:request
                                            completionHandler:^(NSData *data, NSURLResponse *response, NSError *error) {
                                                if (error) {
                                                    NSLog(@"%@", error);
                                                } else {
                                                    NSHTTPURLResponse *httpResponse = (NSHTTPURLResponse *) response;
                                                    NSLog(@"%@", httpResponse);
[dataTask resume];

// Using AFNetworking
// git url :

- (void)processSingleImage: (NSString *)imageName {
    NSData *data = [NSData dataWithContentsOfFile:imageName options:NSDataReadingMapped error:nil];
    NSString *base64String = [data base64EncodedStringWithOptions:0];
    NSDictionary *param = @{@"src" : [NSString stringWithFormat:@"data:image/jpeg;base64,%@", base64String]};

    NSMutableURLRequest *request = [[AFJSONRequestSerializer serializer] requestWithMethod:@"POST" URLString:@"" parameters:param error:nil];
    [request setValue:@"mathpix" forHTTPHeaderField:@"app_id"];
    [request setValue:@"139ee4b61be2e4abcfb1238d9eb99902" forHTTPHeaderField:@"app_key"];

    AFURLSessionManager *manager = [[AFURLSessionManager alloc] initWithSessionConfiguration:[NSURLSessionConfiguration defaultSessionConfiguration]];

    NSURLSessionUploadTask *uploadTask;
    uploadTask = [manager
                  progress:^(NSProgress * _Nonnull uploadProgress) {
                      dispatch_async(dispatch_get_main_queue(), ^{
                  completionHandler:^(NSURLResponse * _Nonnull response, id  _Nullable responseObject, NSError * _Nullable error) {
                      if (error) {
                          NSLog(@"Error: %@", error);
                      } else { //SUCCESS
                          NSLog(@"Response: %@", responseObject);

    [uploadTask resume];

The API returns JSON structured as show here:

    "detection_list": [],
    "detection_map": {
        "contains_chart": 0,
        "contains_diagram": 0,
        "contains_geometry": 0,
        "contains_graph": 0,
        "contains_table": 0,
        "is_inverted": 0,
        "is_not_math": 0,
        "is_printed": 0
    "error": "",
    "latex": "\\lim _ { x \\rightarrow 3} ( \\frac { x ^ { 2} + 9} { x - 3} )",
    "latex_confidence": 0.86757309488734,
    "position": {
        "height": 273,
        "top_left_x": 57,
        "top_left_y": 14,
        "width": 605

Request parameters

An API request must contain two headers (app_id and app_key) and a JSON body with a field named src that specifies an image as a string either containing a public resource reference to the image content or a data uri containing a base64 encoding of the image. The request body may also contain additional optional fields.

The region field, if present, specifies the image area to process using the properties top_left_x, top_left_y, width, and height, which are all in pixel coordinates (integer valued). For example,

    “region”: {
         “top_left_x”: 0, “top_left_y”: 0, “width”: 10, “height”: 10

specifies a 10 x 10 region at the top left of the image.

The ocr field, if present, indicates what kinds of content to recognize. The default value is ["math"]. If users are also interested in reading text, they should pass ocr=["math", "text"]. The text is returned inside a \text environment, such that the Latex output can always be rendered via a reasonable Latex compiler such as Mathjax or Katex.

The confidence_threshold field, if present, sets the minimum acceptable confidence that the expression is 100% correct. If the expression receives a confidence value lower than this number, the result contains an error field and no latex output.

The confidence_rate_threshold field, if present, sets the minimum acceptable acceptable average per character confidence. This field should be used instead of confidence_threshold in applications where the overall accuracy of the system is more important than the accuracy in recognizing the images in question without a single defect. For example, an auto Latex system is useful even if there are a few characters which are not in it’s vocabulary. As long as the algorithm is generally confident, a few missed characters are acceptable as the user can correct these rapidly.

The formats field, if present, contains an array of one or more strings specifying the desired representations for the results. Accepted representations are ‘latex_raw’ to return unmodified output; 'latex_normal’ to remove some extraneous spaces; 'latex_simplified’ to shorten operators, convert periods to \cdot, replace \longdiv with \frac, and replace unknown operators with \mathrm; 'latex_list’ to split an array into lists where appropriate; 'latex_styled’ to improve the visual appearance of the rendered latex; 'mathml’ to return a string containing the MathML for the recognized math; and 'wolfram’ to return a string that is compatible with the Wolfram Alpha engine. In the case of an incompatible result, the server will instead add a mathml_error or wolfram_error field. If formats is not present then the result with contain a latex field with a default representation, where default is normally 'latex_normal’ but can be overridden based on the app_key.

For compatibility the server also accepts url as an alternative to src and a formats field containing latex, latex_confidence_threshold, and latex_confidence_rate_threshold fields. The result for such requests contains a latex field with the specified latex format.

The skip_recrop field indicates whether Mathpix should skip trying to crop out irrelevant parts of the image. By default, it will not do so. This is to benefit apps for which input images contain lots of noise that should be filtered. If images do not contain noise, skip_recrop should be set to True.

Parameter Description
app_id Application identifier (e.g. mymathsolver)
app_key Application key
content_type application/json
src string
region? {top_left_x, top_left_y, width, height}
ocr? [“math”?, “text”?]
skip_recrop? bool
confidence_threshold? number in [0,1]
confidence_rate_threshold number in [0,1]
formats? string[]

Receiving results

The server responds with the results for the image unless the request contains a callback object, in which case the response only contains a single session_id field and the server asynchronously sends the result to the url specified by The asynchronous post contains the result object and a reply object containing the session_id.

An application may override the session_id response by specifying a value for callback.reply in the request. The server will use this value both in the response and the asynchronous post.

Applications that wish to provide additional data in the post may specify a callback.body value. The server will pass this value to the post. If callback.headers is provided then the server will send the contents as headers in the post.

You can create a callback URL for testing simply by visiting Sample code to test the async API option are on the right.

To test the async API, replace the callback URL below with your own by visiting

#!/usr/bin/env python
import sys
import base64
import requests
import json

file_path = "integral.jpg"
image_uri = "data:image/jpeg;base64," + base64.b64encode(open(file_path, "rb").read())
# (python3) image_uri = "data:image/jpg;base64," + base64.b64encode(open(file_path, "rb").read()).decode()
r ="",
    data=json.dumps({'url': image_uri, 'callback': {'post': '', 'reply': file_path}}),
    headers={"app_id": "trial", "app_key": "34f1a4cea0eaca8540c95908b4dc84ab",
        "Content-type": "application/json"})
print r.text
curl -X POST \
    -H 'app_id: trial' \
    -H 'app_key: 34f1a4cea0eaca8540c95908b4dc84ab' \
    -H 'Content-Type: application/json' \
    --data '{ "src": "data:image/jpeg;base64,'$(base64 -i integral.jpg)'","callback":{"post": "", "reply": "integral.jpg"}}'

The following JSON is delivered to the callback URL via a POST request:

  "reply": "integral.jpg",
  "result": {
    "detection_list": [
    "detection_map": {
      "contains_chart": 0,
      "contains_diagram": 0,
      "contains_geometry": 0,
      "contains_graph": 0,
      "contains_table": 0,
      "is_inverted": 0,
      "is_not_math": 0,
      "is_printed": 1
    "error": "",
    "latex": "\\int \\frac { 4 x } { \\sqrt { x ^ { 2 } + 1 } } d x",
    "latex_confidence": 0.99817252453161,
    "latex_list": [],
    "position": {
      "height": 215,
      "top_left_x": 57,
      "top_left_y": 0,
      "width": 605


Example output JSON for the following image is shown on the right.

Confidence values

The API returns confidence scores for the returned latex in the latex_confidence field. It is recommended that you discard results that are below a certain confidence value. You can tune this threshold value for your application. We use a 20% confidence cutoff for our iOS and Android demo apps.

Detection types

The API defines multiple detection types to the client inside the JSON field detection_map. The fields are described below:

Detection Definition
contains_chart Contains a visual representation of a discrete dataset.
contains_table Contains a tabular representation of a discrete dataset.
contains_diagram Contains a diagram.
contains_graph Contains a 1D, 2D, or 3D graph.
contains_geometry Contains chart, table, diagram, or graph.
is_printed The image is taken of printed math, not handwritten math.
is_not_math No valid equation was detected.

The API returns confidence values for each of these detection categories.

Detection coordinates

The position field contains the bounding box of the equation of interest, in pixel coordinates of the image sent. Note that we consider systems of algebraic equations and multiple line equations as single equations. If multiple equations are separated by a lot of text, we will return the first equation only.

Processing a batch of images

A batch request is made as follows:

curl -X POST \
    -H "app_id: trial" \
    -H "app_key: 34f1a4cea0eaca8540c95908b4dc84ab" \
    -H "Content-Type: application/json" \
    --data '{ "urls": {"inverted": "", "algebra": ""},"callback":{"post": ""}}'

import requests
import json

base_url = ''

data = {
    'urls': {
        'algebra': base_url + 'algebra.jpg',
        'inverted': base_url + 'inverted.jpg'
    'callback': {
        'post': ''

r =
    "", data=json.dumps(data),
        'app_id': 'trial',
        'app_key': '34f1a4cea0eaca8540c95908b4dc84ab',
        'content-type': 'application/json'
reply = json.loads(r.text)
assert reply.has_key('batch_id')

The callback response is as follows:

  "reply": {
    "batch_id": "11"
  "result": {
    "algebra": {
      "detection_list": [], 
      "detection_map": {
        "contains_chart": 0, 
        "contains_diagram": 0, 
        "contains_geometry": 0, 
        "contains_graph": 0, 
        "contains_table": 0, 
        "is_inverted": 0, 
        "is_not_math": 0, 
        "is_printed": 0
      "error": "", 
      "latex": "1 2 + 5 x - 8 = 1 2 x - 1 0 ", 
      "latex_confidence": 0.99640350138238, 
      "latex_list": [], 
      "position": {
        "height": 208, 
        "top_left_x": 0, 
        "top_left_y": 0, 
        "width": 1380
    "inverted": {
      "detection_list": [
      "detection_map": {
        "contains_chart": 0, 
        "contains_diagram": 0, 
        "contains_geometry": 0, 
        "contains_graph": 0, 
        "contains_table": 0, 
        "is_inverted": 1, 
        "is_not_math": 0, 
        "is_printed": 1
      "error": "", 
      "latex": "x ^ { 2 } + y ^ { 2 } = 9 ", 
      "latex_confidence": 0.99982263230866, 
      "latex_list": [], 
      "position": {
        "height": 170, 
        "top_left_x": 48, 
        "top_left_y": 85, 
        "width": 544

The Mathpix API supports processing multiple images in a single POST request to a different endpoint: /v3/batch. The request contains urls specifying a key-url pair for each image, and callback that specifies where to send the results. The url in a key-url pair may either be a string or an object containing the url and additional image-specific request parameters such as region and formats. The response contains a unique batch_id value, and after processing all the images the server posts a message to containing result with a key-result pair for each image result and reply containing the batch_id.

An application may specify a value for callback.reply in the request to include fields in addition to batch_id in both the response and result post. However, the server-generated batch_id cannot be overridden.

Applications that wish to provide additional data in the post may specify a callback.body value. The server will pass this value to the post. If callback.headers is provided then the server will send the contents as headers in the post.

To check on the status of a batch request an application may use GET /v3/batch/:id where :id is the returned batch_id. Note that the GET request must contain the same app_id and app_key headers as the batch request. The response body is JSON containing the batch url keys (array of strings), current results (key-result objects), and the callback object passed to the original batch request. The server only keeps batch results for a limited amount of time after the batch is complete (currently two days but could change in the future).

Long division

    "detection_map": {
        "contains_chart": 0,
        "contains_diagram": 0,
        "contains_geometry": 0,
        "contains_graph": 0,
        "contains_table": 0,
        "is_inverted": 0,
        "is_not_math": 0,
        "is_printed": 1
    "latex": "8\\longdiv { 7200}"

The following <script> tags will take care of rendering the \longdiv markup.

<script type="text/javascript" src="//"></script>
<script type="text/x-mathjax-config">
        tex2jax: {inlineMath: [['$','$'], ['\\(','\\)']]},
        messageStyle: "none",
        TeX: {
            Macros: {
               longdiv: ["{\\overline{\\smash{)}#1}}", 1]

We use the special markup \longdiv to represent long division; it is the only nonvalid Latex markup we return. Long division is used much like \sqrt which is visually similar.

Latency considerations

The biggest source of latency is image uploads. The speed of a response from Mathpix API servers is roughly proportional to the size of the image. Try to use images under 100kb for maximum speeds. JPEG compression and image downsizing are recommended to ensure lowest possible latencies.


Mathpix generates any of the following characters: