IsolatedStudy API
Check documents for similarity from your own website, Moodle or app. Send a file, get the report as JSON, and download the PDF – in a few lines of code.
http://scan.isolatedstudy.in/api/v1Authorization: Bearer isk_…JSON · PDFcurl · Python · PHP · JavaScript · Java · C# · Go · RubyHow it works
- 1. Get a key
On the API keys page. It starts with
isk_and is shown once. - 2. Send a document
POST /checkswith a file or text – you get a checkidat once. - 3. Wait for the result
Ask
GET /checks/<id>every few seconds until it isdone(about a minute). - 4. Use the report
Similarity, sources and grade are in the answer – download the PDF from
pdf_urls.
curl http://scan.isolatedstudy.in/api/v1/options -H "Authorization: Bearer isk_your_key_here"
It answers with everything your key may use – formats, filters and word limits.Authentication
Send your key with every request, in one of these two headers:
Authorization: Bearer isk_your_key_here
X-API-Key: isk_your_key_here
Your access
Each API key has its own PDF formats and filters, approved by the site administrator. Using one that is not approved for your key is refused with 403 “You don't have access …” – ask the administrator to enable more.
Limits & queue
PDF formats
Reports come in 8 styles:
classic (Classic), modern (Modern), certificate (Certificate), academic (Academic), minimal (Minimal), executive (Executive), infographic (Infographic), letter (Official letter) – each also in
colour versions, e.g. modern-ocean. A key can only use the formats the administrator approved for it
(never more than its owner's plan). Every report lists them in pdf_formats.
{"error": "This PDF format is not enabled for your API key. Allowed formats: …"}POST /api/v1/checks
Starts a check. Send multipart/form-data with a file, or JSON / form fields with text.
| Field | Type | Description |
|---|---|---|
fileor text | file | The document: PDF (also scanned), Word, PowerPoint, Excel, OpenDocument, RTF, HTML, EPUB, text, code, pictures. |
textor file | string | Plain text instead of a file. |
titleoptional | string | Title of the report (default: from the file name). |
authoroptional | string | Author name shown in the report. |
internetoptional | bool | Search the internet and research databases (default true). |
repositoryoptional | string | none (default, not stored) or standard (stored so later copies are found). |
extrasoptional | string | Comma-separated extra checks: refs, cites, fix, style, own, images, code, translate. |
quotes bib cited min_words min_percent kindsoptional |
filters | Filters applied when the report is made – only the ones approved for your key, see Filters. |
Answer 202:
{"id": "3f9c…", "status": "running", "status_url": "http://scan.isolatedstudy.in/api/v1/checks/3f9c…"}
curl -X POST http://scan.isolatedstudy.in/api/v1/checks \
-H "Authorization: Bearer $API_KEY" \
-F "file=@thesis.docx" \
-F "title=My thesis" \
-F "repository=none"
import requests
API = "http://scan.isolatedstudy.in/api/v1"
HEADERS = {"Authorization": "Bearer isk_your_key_here"}
with open("thesis.docx", "rb") as f:
r = requests.post(f"{API}/checks", headers=HEADERS,
files={"file": f}, data={"title": "My thesis"})
r.raise_for_status()
check_id = r.json()["id"]
<?php
$api = 'http://scan.isolatedstudy.in/api/v1';
$ch = curl_init("$api/checks");
curl_setopt_array($ch, [
CURLOPT_POST => true,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer isk_your_key_here'],
CURLOPT_POSTFIELDS => [
'file' => new CURLFile('thesis.docx'),
'title' => 'My thesis',
],
]);
$check = json_decode(curl_exec($ch), true);
$checkId = $check['id'];
// Node 18+ (fetch, FormData and Blob are built in)
import { readFile } from "node:fs/promises";
const API = "http://scan.isolatedstudy.in/api/v1";
const headers = { Authorization: "Bearer isk_your_key_here" };
const form = new FormData();
form.append("file", new Blob([await readFile("thesis.docx")]), "thesis.docx");
form.append("title", "My thesis");
const res = await fetch(`${API}/checks`, { method: "POST", headers, body: form });
const { id: checkId } = await res.json();
import java.net.URI;
import java.net.http.*;
import java.nio.file.*;
public class Example {
static final String API = "http://scan.isolatedstudy.in/api/v1";
static final String KEY = "isk_your_key_here";
public static void main(String[] args) throws Exception {
HttpClient http = HttpClient.newHttpClient();
String boundary = "----IsolatedStudy" + System.currentTimeMillis();
var body = new java.io.ByteArrayOutputStream();
body.write(("--" + boundary + "\r\nContent-Disposition: form-data; name=\"title\"\r\n\r\nMy thesis\r\n").getBytes());
body.write(("--" + boundary + "\r\nContent-Disposition: form-data; name=\"file\"; filename=\"thesis.docx\"\r\n"
+ "Content-Type: application/octet-stream\r\n\r\n").getBytes());
body.write(Files.readAllBytes(Path.of("thesis.docx")));
body.write(("\r\n--" + boundary + "--\r\n").getBytes());
HttpRequest req = HttpRequest.newBuilder(URI.create(API + "/checks"))
.header("Authorization", "Bearer " + KEY)
.header("Content-Type", "multipart/form-data; boundary=" + boundary)
.POST(HttpRequest.BodyPublishers.ofByteArray(body.toByteArray())).build();
HttpResponse<String> res = http.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.statusCode() + " " + res.body()); // JSON – read it with e.g. Jackson
}
}using System.Net.Http.Headers;
using System.Text.Json;
var API = "http://scan.isolatedstudy.in/api/v1";
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "isk_your_key_here");
using var form = new MultipartFormDataContent();
form.Add(new ByteArrayContent(await File.ReadAllBytesAsync("thesis.docx")), "file", "thesis.docx");
form.Add(new StringContent("My thesis"), "title");
var res = await http.PostAsync($"{API}/checks", form);
res.EnsureSuccessStatusCode();
var data = JsonDocument.Parse(await res.Content.ReadAsStringAsync()).RootElement;
Console.WriteLine(data.GetProperty("id"));package main
import (
"bytes"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
)
const API = "http://scan.isolatedstudy.in/api/v1"
const KEY = "isk_your_key_here"
func main() {
var buf bytes.Buffer
form := multipart.NewWriter(&buf)
form.WriteField("title", "My thesis")
part, _ := form.CreateFormFile("file", "thesis.docx")
file, _ := os.ReadFile("thesis.docx")
part.Write(file)
form.Close()
req, _ := http.NewRequest("POST", API + "/checks", &buf)
req.Header.Set("Authorization", "Bearer "+KEY)
req.Header.Set("Content-Type", form.FormDataContentType())
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
data, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(data))
}require "net/http"
require "json"
API = "http://scan.isolatedstudy.in/api/v1"
KEY = "isk_your_key_here"
uri = URI("#{API}/checks")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{KEY}"
req.set_form([["file", File.open("thesis.docx")], ["title", "My thesis"]], "multipart/form-data")
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") { |h| h.request(req) }
puts JSON.parse(res.body)["id"]
Text instead of a file: curl -X POST http://scan.isolatedstudy.in/api/v1/checks -H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" -d '{"text": "Your text…", "title": "Essay"}'
GET /api/v1/checks/<id>
status is running (with the current step), done (with the full
report) or error (with error). Ask every 3–5 seconds.
{"id": "3f9c…", "status": "done", "elapsed": 58,
"summary": {"words": 4210, "similarity": 18.4, "issues": []},
"report": { …same as GET /api/v1/reports/<id>… }}
curl http://scan.isolatedstudy.in/api/v1/checks/$CHECK_ID \
-H "Authorization: Bearer $API_KEY"
import time
while True:
job = requests.get(f"{API}/checks/{check_id}", headers=HEADERS).json()
if job["status"] == "done":
report = job["report"]
break
if job["status"] == "error":
raise RuntimeError(job["error"])
time.sleep(4)
print(report["similarity"], report["grade"]["letter"])
<?php
do {
sleep(4);
$ch = curl_init("$api/checks/$checkId");
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer isk_your_key_here']]);
$job = json_decode(curl_exec($ch), true);
} while ($job['status'] === 'running');
if ($job['status'] === 'error') { die($job['error']); }
echo $job['report']['similarity'] . '%';
let job;
do {
await new Promise(r => setTimeout(r, 4000));
job = await (await fetch(`${API}/checks/${checkId}`, { headers })).json();
} while (job.status === "running");
if (job.status === "error") throw new Error(job.error);
console.log(job.report.similarity, job.report.grade.letter);
import java.net.URI;
import java.net.http.*;
import java.nio.file.*;
import java.util.regex.Pattern;
public class Example {
static final String API = "http://scan.isolatedstudy.in/api/v1";
static final String KEY = "isk_your_key_here";
public static void main(String[] args) throws Exception {
HttpClient http = HttpClient.newHttpClient();
String checkId = "CHECK_ID";
HttpRequest req = HttpRequest.newBuilder(URI.create(API + "/checks/" + checkId))
.header("Authorization", "Bearer " + KEY)
.GET().build();
Pattern running = Pattern.compile("\"status\"\\s*:\\s*\"running\"");
String json;
do {
Thread.sleep(4000);
json = http.send(req, HttpResponse.BodyHandlers.ofString()).body();
} while (running.matcher(json).find());
System.out.println(json); // read it with a JSON library, e.g. Jackson
}
}using System.Net.Http.Headers;
using System.Text.Json;
var API = "http://scan.isolatedstudy.in/api/v1";
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "isk_your_key_here");
var checkId = "CHECK_ID";
JsonElement job;
do
{
await Task.Delay(4000);
var res = await http.GetAsync($"{API}/checks/{checkId}");
job = JsonDocument.Parse(await res.Content.ReadAsStringAsync()).RootElement;
} while (job.GetProperty("status").GetString() == "running");
Console.WriteLine(job.GetProperty("status").GetString());package main
import (
"encoding/json"
"fmt"
"io"
"net/http"
"time"
)
const API = "http://scan.isolatedstudy.in/api/v1"
const KEY = "isk_your_key_here"
func main() {
checkID := "CHECK_ID"
for {
time.Sleep(4 * time.Second)
req, _ := http.NewRequest("GET", API + "/checks/" + checkID, nil)
req.Header.Set("Authorization", "Bearer "+KEY)
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
data, _ := io.ReadAll(res.Body)
res.Body.Close()
var job map[string]any
json.Unmarshal(data, &job)
if job["status"] != "running" {
fmt.Println(string(data))
return
}
}
}require "net/http"
require "json"
API = "http://scan.isolatedstudy.in/api/v1"
KEY = "isk_your_key_here"
check_id = "CHECK_ID"
uri = URI("#{API}/checks/#{check_id}")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{KEY}"
loop do
sleep 4
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") { |h| h.request(req) }
job = JSON.parse(res.body)
break puts(job["status"], job.dig("report", "similarity")) unless job["status"] == "running"
end
GET /api/v1/reports/<report_id>
The whole report. Add ?text=true to get the document text with every match marked.
| Field | Meaning |
|---|---|
similarity | Overall similarity in % · grade: letter A–D and label |
submission_id | The ID printed on the PDF; anyone can check it on verify_url |
by_kind | % from internet, publications, student papers, your repository |
sources | Each source: n, title, url, kind, percent, exact and paraphrased words |
words, characters | Counted like Microsoft Word |
pdf_formats, pdf_urls | The formats this key may download and their URLs |
download_links | Signed links that work without a key for 7 days (see below) |
text | With ?text=true: pieces {text, source, kind} – kind exact, para or ex |
curl "http://scan.isolatedstudy.in/api/v1/reports/$REPORT_ID?text=true" \
-H "Authorization: Bearer $API_KEY"
report = requests.get(f"{API}/reports/{report_id}", headers=HEADERS,
params={"text": "true"}).json()
for s in report["sources"]:
print(s["n"], s["percent"], s["title"], s.get("url"))
<?php
$ch = curl_init("$api/reports/$reportId?text=true");
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer isk_your_key_here']]);
$report = json_decode(curl_exec($ch), true);
foreach ($report['sources'] as $s) { echo "{$s['percent']}% {$s['title']}\n"; }
const report = await (await fetch(`${API}/reports/${reportId}?text=true`, { headers })).json();
report.sources.forEach(s => console.log(s.n, s.percent, s.title, s.url));
import java.net.URI;
import java.net.http.*;
import java.nio.file.*;
public class Example {
static final String API = "http://scan.isolatedstudy.in/api/v1";
static final String KEY = "isk_your_key_here";
public static void main(String[] args) throws Exception {
HttpClient http = HttpClient.newHttpClient();
String reportId = "REPORT_ID";
HttpRequest req = HttpRequest.newBuilder(URI.create(API + "/reports/" + reportId + "?text=true"))
.header("Authorization", "Bearer " + KEY)
.GET().build();
HttpResponse<String> res = http.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.statusCode() + " " + res.body()); // JSON – read it with e.g. Jackson
}
}using System.Net.Http.Headers;
using System.Text.Json;
var API = "http://scan.isolatedstudy.in/api/v1";
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "isk_your_key_here");
var reportId = "REPORT_ID";
var res = await http.GetAsync($"{API}/reports/{reportId}?text=true");
res.EnsureSuccessStatusCode();
var data = JsonDocument.Parse(await res.Content.ReadAsStringAsync()).RootElement;
Console.WriteLine(data.GetProperty("similarity"));package main
import (
"fmt"
"io"
"net/http"
)
const API = "http://scan.isolatedstudy.in/api/v1"
const KEY = "isk_your_key_here"
func main() {
reportID := "REPORT_ID"
req, _ := http.NewRequest("GET", API + "/reports/" + reportID + "?text=true", nil)
req.Header.Set("Authorization", "Bearer "+KEY)
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
data, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(data))
}require "net/http"
require "json"
API = "http://scan.isolatedstudy.in/api/v1"
KEY = "isk_your_key_here"
report_id = "REPORT_ID"
uri = URI("#{API}/reports/#{report_id}?text=true")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{KEY}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") { |h| h.request(req) }
puts JSON.parse(res.body)["similarity"]
GET /api/v1/reports/<report_id>/pdf?format=classic
The PDF report. format is one of your key's pdf_formats (default: the first one).
curl -L "http://scan.isolatedstudy.in/api/v1/reports/$REPORT_ID/pdf?format=classic" \
-H "Authorization: Bearer $API_KEY" -o report.pdf
pdf = requests.get(f"{API}/reports/{report_id}/pdf", headers=HEADERS,
params={"format": report["pdf_formats"][0]})
pdf.raise_for_status()
open("report.pdf", "wb").write(pdf.content)
<?php
$ch = curl_init("$api/reports/$reportId/pdf?format=classic");
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer isk_your_key_here']]);
file_put_contents('report.pdf', curl_exec($ch));
import { writeFile } from "node:fs/promises";
const pdf = await fetch(`${API}/reports/${reportId}/pdf?format=classic`, { headers });
await writeFile("report.pdf", Buffer.from(await pdf.arrayBuffer()));
import java.net.URI;
import java.net.http.*;
import java.nio.file.*;
public class Example {
static final String API = "http://scan.isolatedstudy.in/api/v1";
static final String KEY = "isk_your_key_here";
public static void main(String[] args) throws Exception {
HttpClient http = HttpClient.newHttpClient();
String reportId = "REPORT_ID";
HttpRequest req = HttpRequest.newBuilder(URI.create(API + "/reports/" + reportId + "/pdf" + "?format=classic"))
.header("Authorization", "Bearer " + KEY)
.GET().build();
http.send(req, HttpResponse.BodyHandlers.ofFile(Path.of("report.pdf")));
System.out.println("Saved report.pdf");
}
}using System.Net.Http.Headers;
var API = "http://scan.isolatedstudy.in/api/v1";
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "isk_your_key_here");
var reportId = "REPORT_ID";
var res = await http.GetAsync($"{API}/reports/{reportId}/pdf?format=classic");
res.EnsureSuccessStatusCode();
await File.WriteAllBytesAsync("report.pdf", await res.Content.ReadAsByteArrayAsync());package main
import (
"fmt"
"io"
"net/http"
"os"
)
const API = "http://scan.isolatedstudy.in/api/v1"
const KEY = "isk_your_key_here"
func main() {
reportID := "REPORT_ID"
req, _ := http.NewRequest("GET", API + "/reports/" + reportID + "/pdf" + "?format=classic", nil)
req.Header.Set("Authorization", "Bearer "+KEY)
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := os.Create("report.pdf")
defer out.Close()
io.Copy(out, res.Body)
fmt.Println("Saved report.pdf")
}require "net/http"
require "json"
API = "http://scan.isolatedstudy.in/api/v1"
KEY = "isk_your_key_here"
report_id = "REPORT_ID"
uri = URI("#{API}/reports/#{report_id}/pdf?format=classic")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{KEY}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") { |h| h.request(req) }
File.binwrite("report.pdf", res.body)
puts "Saved report.pdf"
Download links for your users
download_links in a report holds one signed URL per allowed format, e.g.
{"classic": "http://scan.isolatedstudy.in/api/v1/download/eyJ…"}. They need no key and work for 7 days, so you
can give them to a student or put them in an email. A format the key may no longer use stops working.
Filters
Filters decide what counts towards the similarity. Your key can only use the filters the administrator approved (see Your access). Every filter:
| Filter | Values | What it does | Example |
|---|---|---|---|
quotesExclude quoted text | true / false | Text inside quotation marks does not count towards the similarity. | true |
bibExclude the bibliography | true / false | The reference list / bibliography at the end does not count. | true |
citedExclude cited text | true / false | Sentences with an in-text citation, e.g. (Smith, 2020) or [3], do not count. | true |
min_wordsIgnore small matches | whole number 5–1000 (default 5) | Matching passages shorter than this many words are ignored. | 12 |
min_percentHide small sources | number 0–100 (default 0) | Sources that match less than this % of the document are hidden. | 1 |
kindsSource types | list of: web, publication, source, submission | Only matches from these kinds of sources count (web pages, publications, submitted papers, repository documents). | ["web", "publication"] |
excludeRemove sources | list of source doc_id | Removes chosen sources from the report (their doc_id is in the report's sources). | [123, 456] |
Three ways to use filters
- When you send the document – add them to
POST /api/v1/checks(form fields or JSON), e.g.quotes=true,bib=true,min_words=12,min_percent=1,kinds=web,publication. The report is made with them. - Change them on a report –
POST /api/v1/reports/<report_id>/filtersscores it again and saves them (examples below).excludetakes sourcedoc_ids from the report's sources. - Just for one download – put them in the query string of the PDF or JSON report; nothing is saved:
GET /api/v1/reports/<report_id>/pdf?format=classic"es=true&bib=true&min_words=12
curl -L "http://scan.isolatedstudy.in/api/v1/reports/$REPORT_ID/pdf?format=classic"es=true&bib=true&min_words=12" \
-H "Authorization: Bearer $API_KEY" -o report-filtered.pdf
pdf = requests.get(f"{API}/reports/{report_id}/pdf", headers=HEADERS,
params={"format": "classic", "quotes": "true", "bib": "true", "min_words": 12})
open("report-filtered.pdf", "wb").write(pdf.content)
<?php
$q = http_build_query(['format' => 'classic', 'quotes' => 'true', 'bib' => 'true', 'min_words' => 12]);
$ch = curl_init("$api/reports/$reportId/pdf?$q");
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer isk_your_key_here']]);
file_put_contents('report-filtered.pdf', curl_exec($ch));
const q = new URLSearchParams({ format: "classic", quotes: "true", bib: "true", min_words: "12" });
const pdf = await fetch(`${API}/reports/${reportId}/pdf?${q}`, { headers });
await writeFile("report-filtered.pdf", Buffer.from(await pdf.arrayBuffer()));
import java.net.URI;
import java.net.http.*;
import java.nio.file.*;
public class Example {
static final String API = "http://scan.isolatedstudy.in/api/v1";
static final String KEY = "isk_your_key_here";
public static void main(String[] args) throws Exception {
HttpClient http = HttpClient.newHttpClient();
String reportId = "REPORT_ID";
HttpRequest req = HttpRequest.newBuilder(URI.create(API + "/reports/" + reportId + "/pdf" + "?format=classic"es=true&bib=true&min_words=12"))
.header("Authorization", "Bearer " + KEY)
.GET().build();
http.send(req, HttpResponse.BodyHandlers.ofFile(Path.of("report-filtered.pdf")));
System.out.println("Saved report-filtered.pdf");
}
}using System.Net.Http.Headers;
var API = "http://scan.isolatedstudy.in/api/v1";
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "isk_your_key_here");
var reportId = "REPORT_ID";
var res = await http.GetAsync($"{API}/reports/{reportId}/pdf?format=classic"es=true&bib=true&min_words=12");
res.EnsureSuccessStatusCode();
await File.WriteAllBytesAsync("report-filtered.pdf", await res.Content.ReadAsByteArrayAsync());package main
import (
"fmt"
"io"
"net/http"
"os"
)
const API = "http://scan.isolatedstudy.in/api/v1"
const KEY = "isk_your_key_here"
func main() {
reportID := "REPORT_ID"
req, _ := http.NewRequest("GET", API + "/reports/" + reportID + "/pdf" + "?format=classic"es=true&bib=true&min_words=12", nil)
req.Header.Set("Authorization", "Bearer "+KEY)
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
out, _ := os.Create("report-filtered.pdf")
defer out.Close()
io.Copy(out, res.Body)
fmt.Println("Saved report-filtered.pdf")
}require "net/http"
require "json"
API = "http://scan.isolatedstudy.in/api/v1"
KEY = "isk_your_key_here"
report_id = "REPORT_ID"
uri = URI("#{API}/reports/#{report_id}/pdf?format=classic"es=true&bib=true&min_words=12")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{KEY}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") { |h| h.request(req) }
File.binwrite("report-filtered.pdf", res.body)
puts "Saved report-filtered.pdf"
A filter your key may not use is refused – nothing is changed:
HTTP/1.1 403 Forbidden
{"error": "You don't have access to the filter 'cited' (Exclude cited text). Filters your API key may use: quotes, bib, min_words. Ask the site administrator to enable it.",
"filter": "cited", "allowed_filters": ["quotes", "bib", "min_words"]}
POST /api/v1/reports/<report_id>/filters
Score the report again with other filters (saved with the report). Send only the filters you want to change. The answer is
the updated report, with filters and allowed_filters.
curl -X POST http://scan.isolatedstudy.in/api/v1/reports/$REPORT_ID/filters \
-H "Authorization: Bearer $API_KEY" -H "Content-Type: application/json" \
-d '{"quotes": true, "bib": true, "min_words": 10, "exclude": [123]}'
report = requests.post(f"{API}/reports/{report_id}/filters", headers=HEADERS,
json={"quotes": True, "bib": True, "min_words": 10}).json()
<?php
$ch = curl_init("$api/reports/$reportId/filters");
curl_setopt_array($ch, [CURLOPT_POST => true, CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer isk_your_key_here', 'Content-Type: application/json'],
CURLOPT_POSTFIELDS => json_encode(['quotes' => true, 'bib' => true, 'min_words' => 10])]);
$report = json_decode(curl_exec($ch), true);
const updated = await (await fetch(`${API}/reports/${reportId}/filters`, {
method: "POST", headers: { ...headers, "Content-Type": "application/json" },
body: JSON.stringify({ quotes: true, bib: true, min_words: 10 }),
})).json();
import java.net.URI;
import java.net.http.*;
import java.nio.file.*;
public class Example {
static final String API = "http://scan.isolatedstudy.in/api/v1";
static final String KEY = "isk_your_key_here";
public static void main(String[] args) throws Exception {
HttpClient http = HttpClient.newHttpClient();
String reportId = "REPORT_ID";
HttpRequest req = HttpRequest.newBuilder(URI.create(API + "/reports/" + reportId + "/filters"))
.header("Authorization", "Bearer " + KEY)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString("{\"quotes\": true, \"bib\": true, \"min_words\": 10}")).build();
HttpResponse<String> res = http.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.statusCode() + " " + res.body()); // JSON – read it with e.g. Jackson
}
}using System.Net.Http.Headers;
using System.Net.Http.Json;
using System.Text.Json;
var API = "http://scan.isolatedstudy.in/api/v1";
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "isk_your_key_here");
var reportId = "REPORT_ID";
var res = await http.PostAsJsonAsync($"{API}/reports/{reportId}/filters", new { quotes = true, bib = true, min_words = 10 });
res.EnsureSuccessStatusCode();
var data = JsonDocument.Parse(await res.Content.ReadAsStringAsync()).RootElement;
Console.WriteLine(data.GetProperty("similarity"));package main
import (
"fmt"
"io"
"net/http"
"strings"
)
const API = "http://scan.isolatedstudy.in/api/v1"
const KEY = "isk_your_key_here"
func main() {
reportID := "REPORT_ID"
req, _ := http.NewRequest("POST", API + "/reports/" + reportID + "/filters", strings.NewReader(`{"quotes": true, "bib": true, "min_words": 10}`))
req.Header.Set("Authorization", "Bearer "+KEY)
req.Header.Set("Content-Type", "application/json")
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
data, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(data))
}require "net/http"
require "json"
API = "http://scan.isolatedstudy.in/api/v1"
KEY = "isk_your_key_here"
report_id = "REPORT_ID"
uri = URI("#{API}/reports/#{report_id}/filters")
req = Net::HTTP::Post.new(uri)
req["Authorization"] = "Bearer #{KEY}"
req["Content-Type"] = "application/json"
req.body = { quotes: true, bib: true, min_words: 10 }.to_json
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") { |h| h.request(req) }
puts JSON.parse(res.body)["similarity"]
GET /api/v1/reports?limit=50
This key's reports, newest first (limit up to 200): id, Submission ID, title, date, similarity, words, grade,
download links.
curl "http://scan.isolatedstudy.in/api/v1/reports?limit=20" -H "Authorization: Bearer $API_KEY"
reports = requests.get(f"{API}/reports", headers=HEADERS, params={"limit": 20}).json()["reports"]
<?php
$ch = curl_init("$api/reports?limit=20");
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer isk_your_key_here']]);
$reports = json_decode(curl_exec($ch), true)['reports'];
const { reports } = await (await fetch(`${API}/reports?limit=20`, { headers })).json();
import java.net.URI;
import java.net.http.*;
import java.nio.file.*;
public class Example {
static final String API = "http://scan.isolatedstudy.in/api/v1";
static final String KEY = "isk_your_key_here";
public static void main(String[] args) throws Exception {
HttpClient http = HttpClient.newHttpClient();
HttpRequest req = HttpRequest.newBuilder(URI.create(API + "/reports" + "?limit=20"))
.header("Authorization", "Bearer " + KEY)
.GET().build();
HttpResponse<String> res = http.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.statusCode() + " " + res.body()); // JSON – read it with e.g. Jackson
}
}using System.Net.Http.Headers;
using System.Text.Json;
var API = "http://scan.isolatedstudy.in/api/v1";
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "isk_your_key_here");
var res = await http.GetAsync($"{API}/reports?limit=20");
res.EnsureSuccessStatusCode();
var data = JsonDocument.Parse(await res.Content.ReadAsStringAsync()).RootElement;
Console.WriteLine(data.GetProperty("count"));package main
import (
"fmt"
"io"
"net/http"
)
const API = "http://scan.isolatedstudy.in/api/v1"
const KEY = "isk_your_key_here"
func main() {
req, _ := http.NewRequest("GET", API + "/reports" + "?limit=20", nil)
req.Header.Set("Authorization", "Bearer "+KEY)
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
data, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(data))
}require "net/http"
require "json"
API = "http://scan.isolatedstudy.in/api/v1"
KEY = "isk_your_key_here"
uri = URI("#{API}/reports?limit=20")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{KEY}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") { |h| h.request(req) }
puts JSON.parse(res.body)["count"]
DELETE /api/v1/reports/<report_id>
Deletes the report (and the stored paper, if it was stored). Answer: {"deleted": "<report_id>"}.
curl -X DELETE http://scan.isolatedstudy.in/api/v1/reports/$REPORT_ID -H "Authorization: Bearer $API_KEY"
requests.delete(f"{API}/reports/{report_id}", headers=HEADERS).raise_for_status()
<?php
$ch = curl_init("$api/reports/$reportId");
curl_setopt_array($ch, [CURLOPT_CUSTOMREQUEST => 'DELETE', CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer isk_your_key_here']]);
curl_exec($ch);
await fetch(`${API}/reports/${reportId}`, { method: "DELETE", headers });
import java.net.URI;
import java.net.http.*;
import java.nio.file.*;
public class Example {
static final String API = "http://scan.isolatedstudy.in/api/v1";
static final String KEY = "isk_your_key_here";
public static void main(String[] args) throws Exception {
HttpClient http = HttpClient.newHttpClient();
String reportId = "REPORT_ID";
HttpRequest req = HttpRequest.newBuilder(URI.create(API + "/reports/" + reportId))
.header("Authorization", "Bearer " + KEY)
.DELETE().build();
HttpResponse<String> res = http.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.statusCode() + " " + res.body()); // JSON – read it with e.g. Jackson
}
}using System.Net.Http.Headers;
var API = "http://scan.isolatedstudy.in/api/v1";
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "isk_your_key_here");
var reportId = "REPORT_ID";
var res = await http.DeleteAsync($"{API}/reports/{reportId}");
res.EnsureSuccessStatusCode();
Console.WriteLine(await res.Content.ReadAsStringAsync());package main
import (
"fmt"
"io"
"net/http"
)
const API = "http://scan.isolatedstudy.in/api/v1"
const KEY = "isk_your_key_here"
func main() {
reportID := "REPORT_ID"
req, _ := http.NewRequest("DELETE", API + "/reports/" + reportID, nil)
req.Header.Set("Authorization", "Bearer "+KEY)
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
data, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(data))
}require "net/http"
require "json"
API = "http://scan.isolatedstudy.in/api/v1"
KEY = "isk_your_key_here"
report_id = "REPORT_ID"
uri = URI("#{API}/reports/#{report_id}")
req = Net::HTTP::Delete.new(uri)
req["Authorization"] = "Bearer #{KEY}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") { |h| h.request(req) }
puts res.code, res.body
GET /api/v1/verify/<submission_id>
Is a Submission ID (printed on every PDF) genuine? Works for any report of this site. Answer:
{"valid": true, "created_at": …, "title": …, "similarity": …, "words": …, "pdf_downloads": …} or
{"valid": false}.
curl http://scan.isolatedstudy.in/api/v1/verify/8F3A1C0B9E2D4F67 -H "Authorization: Bearer $API_KEY"
ok = requests.get(f"{API}/verify/8F3A1C0B9E2D4F67", headers=HEADERS).json()["valid"]
<?php
$ch = curl_init("$api/verify/8F3A1C0B9E2D4F67");
curl_setopt_array($ch, [CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer isk_your_key_here']]);
$valid = json_decode(curl_exec($ch), true)['valid'];
const { valid } = await (await fetch(`${API}/verify/8F3A1C0B9E2D4F67`, { headers })).json();
import java.net.URI;
import java.net.http.*;
import java.nio.file.*;
public class Example {
static final String API = "http://scan.isolatedstudy.in/api/v1";
static final String KEY = "isk_your_key_here";
public static void main(String[] args) throws Exception {
HttpClient http = HttpClient.newHttpClient();
HttpRequest req = HttpRequest.newBuilder(URI.create(API + "/verify/8F3A1C0B9E2D4F67"))
.header("Authorization", "Bearer " + KEY)
.GET().build();
HttpResponse<String> res = http.send(req, HttpResponse.BodyHandlers.ofString());
System.out.println(res.statusCode() + " " + res.body()); // JSON – read it with e.g. Jackson
}
}using System.Net.Http.Headers;
using System.Text.Json;
var API = "http://scan.isolatedstudy.in/api/v1";
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "isk_your_key_here");
var res = await http.GetAsync($"{API}/verify/8F3A1C0B9E2D4F67");
res.EnsureSuccessStatusCode();
var data = JsonDocument.Parse(await res.Content.ReadAsStringAsync()).RootElement;
Console.WriteLine(data.GetProperty("valid"));package main
import (
"fmt"
"io"
"net/http"
)
const API = "http://scan.isolatedstudy.in/api/v1"
const KEY = "isk_your_key_here"
func main() {
req, _ := http.NewRequest("GET", API + "/verify/8F3A1C0B9E2D4F67", nil)
req.Header.Set("Authorization", "Bearer "+KEY)
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
data, _ := io.ReadAll(res.Body)
fmt.Println(res.StatusCode, string(data))
}require "net/http"
require "json"
API = "http://scan.isolatedstudy.in/api/v1"
KEY = "isk_your_key_here"
uri = URI("#{API}/verify/8F3A1C0B9E2D4F67")
req = Net::HTTP::Get.new(uri)
req["Authorization"] = "Bearer #{KEY}"
res = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") { |h| h.request(req) }
puts JSON.parse(res.body)["valid"]
GET /api/v1/options
Everything your key accepts: fields, extra checks, its PDF formats, its word limits and every filter with
"allowed": true/false (plus allowed_filters, the list your key may use).
curl http://scan.isolatedstudy.in/api/v1/options -H "Authorization: Bearer $API_KEY"
Errors
Every error is JSON with a message you can show to people: {"error": "a readable message"}.
| Code | What it means | What to do |
|---|---|---|
| 200 / 202 | OK / the check was accepted and is running. | Ask the status until done. |
| 400 | Something is missing or wrong: no file or text, words outside the key's limits, a bad filter value. | Fix the request – the message says what. |
| 401 | No API key, or the key is wrong or revoked. | Check the Authorization header. |
| 403 | No access: a PDF format or a filter that is not approved for your key. | Use one from pdf_formats / allowed_filters, or ask the admin. |
| 404 | Unknown check or report (or one of another key). | Use the id you got back. |
| 413 | The upload is too large for the server. | Send a smaller file. |
| 429 | No checks left on the plan, or the site's limit is reached. | Renew the plan, or try again later. |
| 503 | The service is busy for a moment. | Wait the retry_after seconds and try again. |
Full example: check a file and save the PDF
#!/bin/sh
API=http://scan.isolatedstudy.in/api/v1
KEY=isk_your_key_here
ID=$(curl -s -X POST $API/checks -H "Authorization: Bearer $KEY" -F "file=@thesis.docx" | jq -r .id)
while :; do
JOB=$(curl -s $API/checks/$ID -H "Authorization: Bearer $KEY")
STATUS=$(echo "$JOB" | jq -r .status)
[ "$STATUS" = running ] || break
sleep 4
done
echo "$JOB" | jq '.report.similarity, .report.grade.letter'
REPORT=$(echo "$JOB" | jq -r .report.id)
curl -s "$API/reports/$REPORT/pdf" -H "Authorization: Bearer $KEY" -o report.pdf
import time, requests
API = "http://scan.isolatedstudy.in/api/v1"
HEADERS = {"Authorization": "Bearer isk_your_key_here"}
def check_file(path):
with open(path, "rb") as f:
r = requests.post(f"{API}/checks", headers=HEADERS, files={"file": f}, timeout=120)
r.raise_for_status()
check_id = r.json()["id"]
while True:
job = requests.get(f"{API}/checks/{check_id}", headers=HEADERS, timeout=30).json()
if job["status"] != "running":
break
time.sleep(4)
if job["status"] == "error":
raise RuntimeError(job["error"])
report = job["report"]
pdf = requests.get(report["pdf_url"], headers=HEADERS, timeout=120)
open("report.pdf", "wb").write(pdf.content)
return report
report = check_file("thesis.docx")
print(f"{report['similarity']}% – grade {report['grade']['letter']}")
<?php
const API = 'http://scan.isolatedstudy.in/api/v1';
const KEY = 'isk_your_key_here';
function api($method, $path, $body = null) {
$ch = curl_init(API . $path);
curl_setopt_array($ch, [CURLOPT_CUSTOMREQUEST => $method, CURLOPT_RETURNTRANSFER => true,
CURLOPT_HTTPHEADER => ['Authorization: Bearer ' . KEY]]);
if ($body !== null) { curl_setopt($ch, CURLOPT_POSTFIELDS, $body); }
$out = curl_exec($ch);
return $method === 'PDF' ? $out : json_decode($out, true);
}
$check = api('POST', '/checks', ['file' => new CURLFile('thesis.docx')]);
do { sleep(4); $job = api('GET', '/checks/' . $check['id']); } while ($job['status'] === 'running');
if ($job['status'] === 'error') { die($job['error']); }
$report = $job['report'];
echo $report['similarity'] . '% – grade ' . $report['grade']['letter'] . PHP_EOL;
file_put_contents('report.pdf', file_get_contents($report['download_links'][$report['pdf_formats'][0]]));
// Node 18+
import { readFile, writeFile } from "node:fs/promises";
const API = "http://scan.isolatedstudy.in/api/v1";
const headers = { Authorization: "Bearer isk_your_key_here" };
const sleep = ms => new Promise(r => setTimeout(r, ms));
async function checkFile(path) {
const form = new FormData();
form.append("file", new Blob([await readFile(path)]), path);
const { id } = await (await fetch(`${API}/checks`, { method: "POST", headers, body: form })).json();
let job;
do { await sleep(4000); job = await (await fetch(`${API}/checks/${id}`, { headers })).json(); }
while (job.status === "running");
if (job.status === "error") throw new Error(job.error);
const pdf = await fetch(job.report.pdf_url, { headers });
await writeFile("report.pdf", Buffer.from(await pdf.arrayBuffer()));
return job.report;
}
const report = await checkFile("thesis.docx");
console.log(`${report.similarity}% – grade ${report.grade.letter}`);
import java.net.URI;
import java.net.http.*;
import java.nio.file.*;
import java.util.regex.*;
public class CheckFile {
static final String API = "http://scan.isolatedstudy.in/api/v1";
static final String KEY = "isk_your_key_here";
static final HttpClient http = HttpClient.newHttpClient();
static String field(String json, String name) { // or use a JSON library such as Jackson
Matcher m = Pattern.compile("\"" + name + "\"\\s*:\\s*\"?([^\",}]+)").matcher(json);
return m.find() ? m.group(1) : null;
}
static HttpRequest.Builder call(String path) {
return HttpRequest.newBuilder(URI.create(API + path)).header("Authorization", "Bearer " + KEY);
}
public static void main(String[] args) throws Exception {
// 1. upload
String boundary = "----IsolatedStudy" + System.currentTimeMillis();
var body = new java.io.ByteArrayOutputStream();
body.write(("--" + boundary + "\r\nContent-Disposition: form-data; name=\"file\"; filename=\"thesis.docx\"\r\n"
+ "Content-Type: application/octet-stream\r\n\r\n").getBytes());
body.write(Files.readAllBytes(Path.of("thesis.docx")));
body.write(("\r\n--" + boundary + "--\r\n").getBytes());
String started = http.send(call("/checks")
.header("Content-Type", "multipart/form-data; boundary=" + boundary)
.POST(HttpRequest.BodyPublishers.ofByteArray(body.toByteArray())).build(),
HttpResponse.BodyHandlers.ofString()).body();
String checkId = field(started, "id");
// 2. wait until it is done
String job;
do {
Thread.sleep(4000);
job = http.send(call("/checks/" + checkId).GET().build(), HttpResponse.BodyHandlers.ofString()).body();
} while ("running".equals(field(job, "status")));
if ("error".equals(field(job, "status"))) throw new RuntimeException(field(job, "error"));
// 3. similarity and the PDF
Matcher report = Pattern.compile("\"report\"\\s*:\\s*\\{.*?\"id\"\\s*:\\s*\"(\\w+)\"").matcher(job);
report.find();
System.out.println("Similarity: " + field(job, "similarity") + "%");
http.send(call("/reports/" + report.group(1) + "/pdf").GET().build(),
HttpResponse.BodyHandlers.ofFile(Path.of("report.pdf")));
}
}using System.Net.Http.Headers;
using System.Text.Json;
var API = "http://scan.isolatedstudy.in/api/v1";
var http = new HttpClient();
http.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", "isk_your_key_here");
// 1. upload
using var form = new MultipartFormDataContent();
form.Add(new ByteArrayContent(await File.ReadAllBytesAsync("thesis.docx")), "file", "thesis.docx");
var started = await http.PostAsync($"{API}/checks", form);
started.EnsureSuccessStatusCode();
var checkId = JsonDocument.Parse(await started.Content.ReadAsStringAsync()).RootElement.GetProperty("id").GetString();
// 2. wait until it is done
JsonElement job;
do
{
await Task.Delay(4000);
job = JsonDocument.Parse(await http.GetStringAsync($"{API}/checks/{checkId}")).RootElement;
} while (job.GetProperty("status").GetString() == "running");
if (job.GetProperty("status").GetString() == "error") throw new Exception(job.GetProperty("error").GetString());
// 3. similarity and the PDF
var report = job.GetProperty("report");
Console.WriteLine($"{report.GetProperty("similarity")}% – grade {report.GetProperty("grade").GetProperty("letter")}");
var pdf = await http.GetByteArrayAsync($"{API}/reports/{report.GetProperty("id").GetString()}/pdf");
await File.WriteAllBytesAsync("report.pdf", pdf);package main
import (
"bytes"
"encoding/json"
"fmt"
"io"
"mime/multipart"
"net/http"
"os"
"time"
)
const API = "http://scan.isolatedstudy.in/api/v1"
const KEY = "isk_your_key_here"
func call(method, path string, body io.Reader, ctype string) []byte {
req, _ := http.NewRequest(method, API+path, body)
req.Header.Set("Authorization", "Bearer "+KEY)
if ctype != "" {
req.Header.Set("Content-Type", ctype)
}
res, err := http.DefaultClient.Do(req)
if err != nil {
panic(err)
}
defer res.Body.Close()
data, _ := io.ReadAll(res.Body)
return data
}
func main() {
// 1. upload
var buf bytes.Buffer
form := multipart.NewWriter(&buf)
part, _ := form.CreateFormFile("file", "thesis.docx")
file, _ := os.ReadFile("thesis.docx")
part.Write(file)
form.Close()
var started struct{ ID string `json:"id"` }
json.Unmarshal(call("POST", "/checks", &buf, form.FormDataContentType()), &started)
// 2. wait until it is done
var job struct {
Status string `json:"status"`
Error string `json:"error"`
Report struct {
ID string `json:"id"`
Similarity float64 `json:"similarity"`
} `json:"report"`
}
for job.Status == "" || job.Status == "running" {
time.Sleep(4 * time.Second)
json.Unmarshal(call("GET", "/checks/"+started.ID, nil, ""), &job)
}
if job.Status == "error" {
panic(job.Error)
}
// 3. similarity and the PDF
fmt.Printf("Similarity: %.1f%%\n", job.Report.Similarity)
os.WriteFile("report.pdf", call("GET", "/reports/"+job.Report.ID+"/pdf", nil, ""), 0644)
}require "net/http"
require "json"
API = "http://scan.isolatedstudy.in/api/v1"
KEY = "isk_your_key_here"
def call(req)
uri = req.uri
req["Authorization"] = "Bearer #{KEY}"
Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") { |h| h.request(req) }
end
# 1. upload
upload = Net::HTTP::Post.new(URI("#{API}/checks"))
upload.set_form([["file", File.open("thesis.docx")]], "multipart/form-data")
check_id = JSON.parse(call(upload).body)["id"]
# 2. wait until it is done
job = nil
loop do
sleep 4
job = JSON.parse(call(Net::HTTP::Get.new(URI("#{API}/checks/#{check_id}"))).body)
break unless job["status"] == "running"
end
raise job["error"] if job["status"] == "error"
# 3. similarity and the PDF
report = job["report"]
puts "#{report["similarity"]}% – grade #{report["grade"]["letter"]}"
pdf = call(Net::HTTP::Get.new(URI("#{API}/reports/#{report["id"]}/pdf")))
File.binwrite("report.pdf", pdf.body)