API Basics

API Introduction

The Readable.com API is a simple interface for passing text and URLs to Readable.com from your application and getting back readability and keyword density statistics.

You can see your usage of the API in your account administration area.

All responses from the API are in JSON format. Each response contains a field called "result", which will contain either "error" or "success". If the "result" is "error", more information about the error will be contained in the "messages" array field. Every response also contains a "response_timestamp" field.

API Authentication

Authentication of all requests is done with a pair of (case-insensitive) HTTP headers - API_SIGNATURE and API_REQUEST_TIME.

API_REQUEST_TIME is the UNIX timestamp of the current time and API_SIGNATURE is an MD5 hash of an API key and this exact timestamp (example). API keys can be generated in your account administration area (you can have as many as you want).

You should never send your API key directly with a request.

HEADER Parameters

  • API_REQUEST_TIME
    Required

    The API_REQUEST_TIME header is sent with every request to the API, and is the UNIX timestamp of the request (in the UTC timezome). The request will be rejected if the API_REQUEST_TIME is not within the last 30 seconds.

  • API_SIGNATURE
    Required

    The API_SIGNATURE header is an MD5 hash of an API key and the time you set in the API_REQUEST_TIME header. It should change with every request.

API Testing

If you want to test the API, you can use the following testing API key.

Test API Key: THISISATESTKEYTHEREAREMANYLIKEIT

Please note that the requests made with the test key return the same results in the same format as a normal request, but the API will not analyse the text you send; it analyses pre-set test text (if you're curious, the first paragraph of Moby Dick). There is no charge for test requests.

API Endpoints

Analyse Text

You can pass any plain text to the site to score.

POST https://api.readable.com/api/text/

POST Parameters

  • text
    Required

    Type: Plain text string, with UTF-8 encoding
    Description: The text you wish to score.

Analyse a URL

You can pass any URL to the site to score. We recommend sending all URLs encoded as UTF-8. If a URL is resolved to a file, that will automatically be added to the file queue to process as though it were uploaded through the website.

POST https://api.readable.com/api/url/

POST Parameters

  • url
    Required

    Type: URL string, with UTF-8 encoding
    Description: The URL you wish to score.

  • extract
    Optional

    Type: boolean, true or false (defaults to false)
    Description: If set to true, we will attempt to automatically extract the body copy from the URL, removing navigation, headers, footers and so on.

Retrieve Highlighted Issues and Content from a Scored Item

When you score a piece of text or a URL with the API, the API response will include a score_id. You can pass this back to the API highlight endpoint to retrieve a highlighted version of that content, showing possible issues to address.

Text highlights are only available for six hours after scoring a URL or piece of text.

The highlighted version of the text is provided in HTML, with spans wrapped around items of interest. The API response includes a key for the classes for those spans to indicate what they mean.

POST https://api.readable.com/api/highlight/

POST Parameters

  • score_id
    Required

    Type: score ID string, with UTF-8 encoding
    Description: The score ID, returned with the results of a previous call to either the text or URL endpoint.

Profanity Detector

You can pass any plain text to the API to be examined for profanity. You can also specify how sensitive you would like the profanity detector to be.

POST https://api.readable.com/api/profanity/

POST Parameters

  • text
    Required

    Type: Plain text string, with UTF-8 encoding
    Description: The text you wish to analyse.

  • level
    Optional

    Type: A number - 1, 2 or 3 (defaults to 1)
    Description: The sensitivity you want to set the profanity detection systems to. 1: Highlight strong profanity only (e.g., "fuck", "arse", racism and other discriminatory language). 2: Highlight strong and medium profanity including mildly offensive words (e.g., "idiot", "dammit", "wanker"). 3: Highlight all potential profanity, including words which are usually fine but can be profane or objectionable in certain circumstances (e.g., "black", "yank", "addict").

API Examples

API Postman Example

Please click the button below to load an example of our API using Postman (please note that this uses a test key for the API and returns example data in response to each query).

API Text Request Example (PHP)

<?php
$api_key = 'THISISATESTKEYTHEREAREMANYLIKEIT'; // Generate this in your Readable.com account.
$url = 'https://api.readable.com/api/text/'; // The API endpoint you want to interact with
$request_time = time(); // Time of request
$api_signature = md5($api_key . $request_time); // Generate signature
$text = 'The quick brown fox jumps over the lazy dog.'; // The text you want to score

// Fetch URL with CURL
$postItems = array('text' => $text);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postItems));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('API_SIGNATURE: ' . $api_signature, 'API_REQUEST_TIME: ' . $request_time));
$json = curl_exec($ch);
curl_close($ch);

$results = json_decode($json, true);
var_dump($results);

API Text Response Example

{
  "paragraph_count": 1,
  "sentence_count": 8,
  "sentence_count_flesch": 11,
  "letter_count": 884,
  "word_count": 201,
  "lensear_word_count": 122,
  "syllable_count": 301,
  "unique_word_count": 134,
  "word_with_three_syllables": 22,
  "word_with_three_syllables_common_only": 20,
  "dale_chall_difficult_words": 38,
  "spache_difficult_words": 51,
  "cefr_level": "B1",
  "ielts_level": "4-5",
  "composition_adjective_count": 15,
  "composition_adverb_count": 11,
  "composition_conjunction_count": 17,
  "composition_determiner_count": 30,
  "composition_interjection_count": 1,
  "composition_noun_count": 48,
  "composition_preposition_count": 25,
  "composition_pronoun_count": 32,
  "composition_proper_noun_count": 3,
  "composition_qualifier_count": 5,
  "composition_verb_count": 18,
  "composition_unknown_count": 0,
  "composition_nonword_count": 0,
  "flesch_reading_ease": "61.6",
  "flesch_kincaid_grade_level": "9.2",
  "gunning_fog_score": "11.3",
  "powers_sumner_kearl_score": "6.3",
  "coleman_liau_index": "8.9",
  "automated_readability_index": "11.8",
  "smog_index": "12.6",
  "dale_chall_readability_score": "4.2",
  "spache_readability_score": "5.8",
  "forcast_grade": "9.9",
  "lix_score": 45,
  "rix_score": 10,
  "lensear_write": "72.6",
  "raygor_grade": 9,
  "fry_grade": 9,
  "longest_word_syllables": "6 ([\"involuntarily\"])",
  "longest_word_letters": "13 ([\"involuntarily\",\"philosophical\"])",
  "longest_sentence_words": "87 ([\"whenever i find myself growing grim about the mouth; whenever it is a damp drizzly november in my soul; whenever i find myself involuntarily pausing before coffin warehouses and bringing up the rear of every funeral i meet and especially whenever my hypos get such an upper hand of me that it requires a strong moral principle to prevent me from deliberately stepping into the street and methodically knocking people's hats off then i account it high time to get to sea as soon as i can\"]])",
  "tone_number": 50,
  "sentiment": "Positive",
  "sentiment_number": 74,
  "personal_number": 0,
  "gender_number": 30,
  "rating": "D",
  "sentences_per_paragraph": "8.0",
  "words_per_paragraph": "201.0",
  "words_per_sentence": "25.1",
  "words_per_sentence_flesch": "18.3",
  "syllables_per_word": "1.5",
  "letters_per_word": "4.4",
  "average_grade_level": "10.8",
  "reading_time": "0:53",
  "speaking_time": "1:36",
  "gender": "Female",
  "tone": "Neutral",
  "personal": "Impersonal",
  "keyword_density": {
    "1 word": {
      "0000000009-I": {
        "item": "I",
        "count": 9,
        "percentage": "4.48"
      },
      "0000000004-is": {
        "item": "is",
        "count": 4,
        "percentage": "1.99"
      },
      "0000000002-time": {
        "item": "time",
        "count": 2,
        "percentage": "1.00"
      },
      "0000000002-little": {
        "item": "little",
        "count": 2,
        "percentage": "1.00"
      },
      "0000000002-get": {
        "item": "get",
        "count": 2,
        "percentage": "1.00"
      },
      "0000000002-find": {
        "item": "find",
        "count": 2,
        "percentage": "1.00"
      }
    },
    "2 words": {
      "0000000004-find myself": {
        "item": "find myself",
        "count": 2,
        "percentage": "1.00"
      },
      "0000000004-I find": {
        "item": "I find",
        "count": 2,
        "percentage": "1.00"
      }
    },
    "3 words": {
      "0000000006-I find myself": {
        "item": "I find myself",
        "count": 2,
        "percentage": "1.00"
      }
    }
  },
  "reach": "91",
  "reach_public": "77",
  "flesch_kincaid_reading_ease": "61.6",
  "score_id": "373150f3762e359f19f0",
  "server_reference": "web3",
  "result": "success",
  "response_timestamp": 1650614743
}

API URL Request Example (PHP)

<?php
$api_key = 'THISISATESTKEYTHEREAREMANYLIKEIT'; // Generate this in your Readable.com account.
$api_url = 'https://api.readable.com/api/url/'; // The API endpoint you want to interact with
$request_time = time(); // Time of request
$api_signature = md5($api_key . $request_time); // Generate signature
$url = 'http://www.example.com/'; // The url you want to score
$extract = true; // Whether or not to extract the text from the url (removing nav, header, footer, etc.)

// Fetch URL with CURL
$postItems = compact('url', 'extract');
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $api_url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postItems));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('API_SIGNATURE: ' . $api_signature, 'API_REQUEST_TIME: ' . $request_time));
$json = curl_exec($ch);
curl_close($ch);

$results = json_decode($json, true);
var_dump($results);

API URL Response Example

{
  "paragraph_count": 1,
  "sentence_count": 8,
  "sentence_count_flesch": 11,
  "letter_count": 884,
  "word_count": 201,
  "lensear_word_count": 122,
  "syllable_count": 301,
  "unique_word_count": 134,
  "word_with_three_syllables": 22,
  "word_with_three_syllables_common_only": 20,
  "dale_chall_difficult_words": 38,
  "spache_difficult_words": 51,
  "cefr_level": "B1",
  "ielts_level": "4-5",
  "composition_adjective_count": 15,
  "composition_adverb_count": 11,
  "composition_conjunction_count": 17,
  "composition_determiner_count": 30,
  "composition_interjection_count": 1,
  "composition_noun_count": 48,
  "composition_preposition_count": 25,
  "composition_pronoun_count": 32,
  "composition_proper_noun_count": 3,
  "composition_qualifier_count": 5,
  "composition_verb_count": 18,
  "composition_unknown_count": 0,
  "composition_nonword_count": 0,
  "flesch_reading_ease": "61.6",
  "flesch_kincaid_grade_level": "9.2",
  "gunning_fog_score": "11.3",
  "powers_sumner_kearl_score": "6.3",
  "coleman_liau_index": "8.9",
  "automated_readability_index": "11.8",
  "smog_index": "12.6",
  "dale_chall_readability_score": "4.2",
  "spache_readability_score": "5.8",
  "forcast_grade": "9.9",
  "lix_score": 45,
  "rix_score": 10,
  "lensear_write": "72.6",
  "raygor_grade": 9,
  "fry_grade": 9,
  "longest_word_syllables": "6 ([\"involuntarily\"])",
  "longest_word_letters": "13 ([\"involuntarily\",\"philosophical\"])",
  "longest_sentence_words": "87 ([\"whenever i find myself growing grim about the mouth; whenever it is a damp drizzly november in my soul; whenever i find myself involuntarily pausing before coffin warehouses and bringing up the rear of every funeral i meet and especially whenever my hypos get such an upper hand of me that it requires a strong moral principle to prevent me from deliberately stepping into the street and methodically knocking people's hats off then i account it high time to get to sea as soon as i can\"]])",
  "tone_number": 50,
  "sentiment": "Positive",
  "sentiment_number": 74,
  "personal_number": 0,
  "gender_number": 30,
  "rating": "D",
  "sentences_per_paragraph": "8.0",
  "words_per_paragraph": "201.0",
  "words_per_sentence": "25.1",
  "words_per_sentence_flesch": "18.3",
  "syllables_per_word": "1.5",
  "letters_per_word": "4.4",
  "average_grade_level": "10.8",
  "reading_time": "0:53",
  "speaking_time": "1:36",
  "gender": "Female",
  "tone": "Neutral",
  "personal": "Impersonal",
  "keyword_density": {
    "1 word": {
      "0000000009-I": {
        "item": "I",
        "count": 9,
        "percentage": "4.48"
      },
      "0000000004-is": {
        "item": "is",
        "count": 4,
        "percentage": "1.99"
      },
      "0000000002-time": {
        "item": "time",
        "count": 2,
        "percentage": "1.00"
      },
      "0000000002-little": {
        "item": "little",
        "count": 2,
        "percentage": "1.00"
      },
      "0000000002-get": {
        "item": "get",
        "count": 2,
        "percentage": "1.00"
      },
      "0000000002-find": {
        "item": "find",
        "count": 2,
        "percentage": "1.00"
      }
    },
    "2 words": {
      "0000000004-find myself": {
        "item": "find myself",
        "count": 2,
        "percentage": "1.00"
      },
      "0000000004-I find": {
        "item": "I find",
        "count": 2,
        "percentage": "1.00"
      }
    },
    "3 words": {
      "0000000006-I find myself": {
        "item": "I find myself",
        "count": 2,
        "percentage": "1.00"
      }
    }
  },
  "reach": "91",
  "reach_public": "77",
  "flesch_kincaid_reading_ease": "61.6",
  "score_id": "4ed236d922b6e30365b6",
  "result_note": "We could not extract the text content of this page so we scored the entire page.",
  "server_reference": "web1",
  "result": "success",
  "response_timestamp": 1650616274
}

API Highlight Request Example (PHP)

<?php
$api_key = 'THISISATESTKEYTHEREAREMANYLIKEIT'; // Generate this in your Readable.com account.
$url = 'https://api.readable.com/api/highlight/'; // The API endpoint you want to interact with
$request_time = time(); // Time of request
$api_signature = md5($api_key . $request_time); // Generate signature
$score_id = '4ed236d922b6e30365b6'; // The score ID, returned with the results of a previous call to either the text or URL endpoint

// Fetch URL with CURL
$postItems = array('score_id' => $score_id);
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postItems));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('API_SIGNATURE: ' . $api_signature, 'API_REQUEST_TIME: ' . $request_time));
$json = curl_exec($ch);
curl_close($ch);

$results = json_decode($json, true);
var_dump($results);

API Highlight Response Example

{
  "db_table": "result",
  "very_long_sentence_count": 3,
  "spelling_error_count": 0,
  "grammar_error_count": 0,
  "long_sentence_count": 5,
  "passive_voice_count": 0,
  "adverb_count": 12,
  "cliche_count": 0,
  "hedge_count": 0,
  "transition_count": 0,
  "profanity_count": 0,
  "buzzwords_count": 0,
  "names_count": 0,
  "lazy_count": 0,
  "stopwords_count": 0,
  "long_word_count": 0,
  "high_syllable_word_count": 4,
  "highlighted_text": "Call me Ishmael. Some years ago - never mind how long precisely - having little or no money in my purse, and nothing particular to interest me on shore, I thought I would sail about a little and see the watery part of the world. It is a way I have of driving off the spleen, and regulating the circulation. Whenever I find myself growing grim about the mouth; whenever it is a damp, drizzly November in my soul; whenever I find myself involuntarily pausing before coffin warehouses, and bringing up the rear of every funeral I meet; and especially whenever my hypos get such an upper hand of me, that it requires a strong moral principle to prevent me from deliberately stepping into the street, and methodically knocking people's hats off - then, I account it high time to get to sea as soon as I can. This is my substitute for pistol and ball. With a philosophical flourish Cato throws himself upon his sword; I quietly take to the ship. There is nothing surprising in this. If they but knew it, almost all men in their degree, some time or other, cherish very nearly the same feelings towards the ocean with me.",
  "highlighted_text_key": {
    "highlight_warn": "Long sentence (more than 20 syllables)",
    "highlight_bad": "Very long sentence (more than 30 syllables)",
    "highlight_long_word": "Long word (more than 12 letters)",
    "highlight_hard_word": "Hard word (more than 4 syllables)",
    "highlight_adverb": "Possible adverb",
    "highlight_cliche": "Possible cliche",
    "highlight_passive": "Possible passive voice",
    "highlight_hedge": "Possible Hedge Word",
    "highlight_transition": "Possible Transition Word",
    "highlight_profanity": "Possible Profanity",
    "highlight_buzzwords": "Possible Buzzword",
    "highlight_stopwords": "Possible Stop Word",
    "highlight_lazy": "Possible Lazy Words",
    "highlight_names": "Possible Names"
  },
  "score_id": "4ed236d922b6e30365b6",
  "server_reference": "web1",
  "result": "success",
  "response_timestamp": 1650616693
}

API Profanity Detector Request Example (PHP)

<?php
$api_key = 'THISISATESTKEYTHEREAREMANYLIKEIT'; // Generate this in your Readable.com account.
$url = 'https://api.readable.com/api/profanity/'; // The API endpoint you want to interact with
$request_time = time(); // Time of request
$api_signature = md5($api_key . $request_time); // Generate signature
$text = 'You should definitely not use words like idiot or wanker in a business setting.'; // The text you want to score
$level = 3; // The level of profanity you want to check for

// Fetch URL with CURL
$postItems = compact('text', 'level');
$ch = curl_init();
curl_setopt($ch, CURLOPT_URL, $url);
curl_setopt($ch, CURLOPT_POST, 1);
curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($postItems));
curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
curl_setopt($ch, CURLOPT_HTTPHEADER, array('API_SIGNATURE: ' . $api_signature, 'API_REQUEST_TIME: ' . $request_time));
$json = curl_exec($ch);
curl_close($ch);

$results = json_decode($json, true);
var_dump($results);

API Profanity Detector Response Example

{
  "profanities": [
    {
      "profanity": "idiot",
      "type": "misc / other",
      "source": "idiot",
      "start": 41,
      "length": 5
    },
    {
      "profanity": "wanker",
      "type": "sexual",
      "source": "wanker",
      "start": 50,
      "length": 6
    }
  ],
  "profanity_count": 2,
  "server_reference": "web1",
  "result": "success",
  "response_timestamp": 1650617482
}

API Response Fields

Our API calls return various fields and an explanation of each is below.

Common Response Fields

  • score_id

    A unique ID for the request (used when subsequent processing is required on a piece of text).

  • result

    Either "success" or "failure".

  • response_timestamp

    A UNIX timestamp indicating the time the response was sent by the server.

Readability Algorithms

  • rating

    Our proprietary readability score, from A to E.

  • flesch_reading_ease
    flesch_kincaid_grade_level
    gunning_fog_score
    coleman_liau_index
    smog_index
    automated_readability_index
    forcast_grade
    fry_grade
    raygor_grade
    lensear_write
    lix_score
    rix_score
    dale_chall_readability_score
    spache_readability_score
    powers_sumner_kearl_score
    cefr_level
    ielts_level
    cefr_score

    Readability scores, for which more details can be found here.

  • average_grade_level

    A deprecated average measure of grade scores. We do not recommend using this score, unless you were already conducting research using this measure.

Basic Statistics

  • paragraph_count
    sentence_count
    letter_count
    word_count
    unique_word_count
    word_with_three_syllables
    syllable_count

    Simple counts of items within the text.

  • sentence_count_flesch

    The sentence count as used in various readability scores (where a semi-colon is taken to be a sentence terminator).

  • lensear_word_count

    The number of words in the text which appear in the Lensear word list.

  • word_with_three_syllables_common_only

    Common words with three syllables.

  • dale_chall_difficult_words
    spache_difficult_words

    The number of words found in the content which do not appear on the respective word lists for each algorithm.

  • words_per_sentence
    sentences_per_paragraph
    words_per_paragraph
    words_per_sentence_flesch
    syllables_per_word
    letters_per_word

    Pre-calculated averages of some of the basic statistics from the text.

  • composition_adjective_count
    composition_adverb_count
    composition_conjunction_count
    composition_determiner_count
    composition_interjection_count
    composition_noun_count
    composition_proper_noun_count
    composition_preposition_count
    composition_pronoun_count
    composition_qualifier_count
    composition_verb_count
    composition_unknown_count
    composition_nonword_count

    The composition of the text, including the number of nouns, number of verbs, and so on. The "unknown" items refer to words we did not have a classification for. The "nonword" items include numbers, symbols and other non-words.

  • reading_time
    speaking_time

    The reading or speaking time for the text, in the format M:SS (e.g., "2:05" - two minutes and five seconds).

  • longest_word_letters

    The longest word by letter count.

  • longest_word_syllables

    The longest word by syllable count.

  • longest_sentence_words

    The longest sentence by word count.

Analysis

  • sentiment
    sentiment_number

    The sentiment as text and as a number from 0 to 100 (where 0 is very negative and 100 is very positive).

  • tone
    tone_number

    The tone as text and as a number from 0 to 100 (where 0 is very formal and 100 is very informal).

  • personal
    personal_number

    The personalism as text and as a number from 0 to 100 (where 0 is very impersonal and 100 is very personal).

  • gender
    gender_number

    The gender as text and as a number from 0 to 100 (where 0 is very female and 100 is very male).

  • reach
    reach_public

    The reach as as percentage of your addressable audience and the reach as a percentage of the general public.

Highlighting Issues

  • highlighted_text

    A string of HTML with issues and highlighted items wrapped in spans.

  • highlighted_text_key

    A JSON-encoded array of the span classes used in the highlighted HTML.

  • spelling_error_count
    grammar_error_count
    very_long_sentence_count
    long_sentence_count
    long_word_count
    high_syllable_word_count
    cliche_count
    adverb_count
    passive_voice_count
    hedge_count
    transition_count
    profanity_count
    buzzwords_count
    names_count
    lazy_count
    stopwords_count

    The number of issues of each type we found and highlighted.

API Example Classes and Code

These code examples have been generously shared by ReadablePro API users and are provided as-is, with no guarantee or warantee.

Login Popup Trigger

Log in to Readable

Login with Readable

          Don't have an account yet?

          Sign Up Today

          Premium Popup Trigger

          Measuring Readability Score ...