Back to use cases

    Use case / AI SEARCH / Python 3 / Published

    Build a plagiarism checker in Python

    Build a plagiarism checker in Python that finds public web pages sharing sentences with your text, with a match score, the passage and a cost per run.

    Sampled sentences flowing through Desearch web search into scored matching pages

    This plagiarism checker finds public web pages that share sentences with your text, with a similarity score and the matching passage for each page. It is a Python CLI. This page shows a stored sample; there is no hosted demo.

    The problem

    You have an essay, an article or a product description and want to know whether parts of it already appear on the public web. Pasting sentences into a search box one by one is slow. You also need a score you can compare, the passage that matched and a record of what each check cost.

    What the app does

    • Samples the most distinctive sentences of a file, stdin or inline text.
    • Searches the web for each sentence and scores every returned snippet.
    • Fetches the best candidate pages once and compares them with every sampled sentence.
    • Writes a matches CSV, a sentences CSV and a Markdown summary with grouped sources.
    • Shows a plan and cost estimate first, and stops above a cost limit you set.

    The tool gives no verdict and does not detect AI writing. Only sampled sentences are checked, and only pages the search returns are compared. A match does not establish who wrote the wording first.

    How it works

    1. Sample sentences. The CLI splits the text at sentence punctuation, skips short sentences and picks up to 8 that have the most substantial words. Long sentences are shortened to their most distinctive window of up to 18 words.
    2. Search. The default engine sends each sentence as a quoted query to GET /web. The search engine uses POST /search with page text instead. It costs more per sentence and can miss very new pages.
    3. Score snippets. Text is normalized for Unicode, case and punctuation. An exact match scores 1. Otherwise the score is the share of the sentence's word triples found in the snippet.
    4. Verify pages. Up to 3 top pages per sentence, and 10 per run, are fetched with GET /web/extract as plain text. Each page is scored against every sampled sentence at no extra cost.
    5. Report. Matches at or above the threshold (0.6 by default) go to the CSV with the URL, score, longest shared word run and a passage of about 200 characters. The summary groups mirror URLs under one source.

    The code

    This verbatim excerpt from plagiarism_checker.py scores one result. It relies on difflib, re and the normalize helper defined in the full file.

    PYTHON
    def score_result(sentence, result):
        page = result.get('page_text') or ' '.join(result.get('highlights') or [])
        a, b = normalize(sentence).split(), normalize(page).split()
        exact = bool(a) and ' '.join(a) in ' '.join(b)
        n = 3 if len(a) >= 3 else 2
        grams = [tuple(a[i:i+n]) for i in range(len(a)-n+1)]
        pagegrams = {tuple(b[i:i+n]) for i in range(len(b)-n+1)}
        containment = sum(g in pagegrams for g in grams) / len(grams) if grams else 0.0
        run = difflib.SequenceMatcher(None, a, b, autojunk=False).find_longest_match()
        # Map normalized words back to original character offsets for readable passages.
        offsets = []
        for match in re.finditer(r'\w+', page, re.UNICODE):
            offsets.extend([match.start()] * len(normalize(match.group()).split()))
        start = offsets[run.b] if run.b < len(offsets) else 0
        start = max(0, start - 70)
        snippet = page[start:start+200]
        return dict(score=1.0 if exact else containment, exact=exact, containment=containment,
                    longest_run_words=run.size, longest_run_share=run.size/len(a) if a else 0,
                    evidence_snippet=snippet)

    View the code

    Endpoints

    • GET /web
    • GET /web/extract
    • POST /search

    Example output

    From the source README, a run on samples/mixed-essay.txt on 2026-10-24. The sample text mixes original sentences with one encyclopedia sentence, one public-domain book sentence and one paraphrase.

    Matching sources found on the web
    3 of 7 sampled sentences have at least one matching public page (42.9%).
    Total cost: $0.010000 USD; 14 calls; slowest 2.95 s; empty-result calls 0 ($0.000000).

    When no sampled sentence matches, the report says so and adds that this does not prove the text is original.

    Run it yourself

    Get the Python examples and open the plagiarism checker folder. Use Python 3.9 or newer. Create an API key in the console. The CLI reads the key only from a private file: set DESEARCH_KEY_FILE or use the default ~/.desearch_api_key.

    BASH
    git clone https://github.com/Desearch-ai/desearch-py-examples
    cd desearch-py-examples/plagiarism-checker
    python3 -m venv .venv
    . .venv/bin/activate
    python -m pip install -r requirements.txt

    These commands are copied from the source README. The first shows the plan and estimate without a key or network.

    SH
    python plagiarism_checker.py essay.txt --dry-run
    python plagiarism_checker.py essay.txt --yes --out output --run-name essay-check
    python plagiarism_checker.py --text 'Carefully designed gardens support diverse flowering plants throughout the summer.' --yes

    Use --max-sentences, --verify-pages and --max-cost to limit a run. --replay rebuilds reports from saved responses with no new calls.

    What it costs

    The 2026-10-24 sample run made 7 searches and 7 page fetches. Six fetches were billed and one returned 404 at $0. The total was $0.010, read from X-Desearch-Cost-Usd response headers.

    The default estimate uses $0.001 per web search and $0.0005 per page extraction, so 8 sentences with page checks stay well under the default --max-cost of $0.25. These are dated measurements and list rates, not current price quotes. Check pricing before a larger run.

    Ideas to extend it

    • Run the checker on every new article before it goes live and keep the summary with the draft.
    • Compare two runs of the same text to see which matching pages are new.
    • Add a review step that opens each matching page next to its passage.
    • Raise --max-sentences for long documents and set --max-cost to match.

    Use with AI agents

    Point an MCP client at the hosted Desearch server. Replace the placeholder with a key from the console. Do not commit the key.

    JSON
    {
      "mcpServers": {
        "desearch": {
          "url": "https://mcp.desearch.ai/mcp",
          "headers": {
            "Authorization": "Bearer <key>"
          }
        }
      }
    }

    Prompt you can paste for this example:

    Build a Python CLI plagiarism checker using Desearch. Read the API key from a private key file. Split the input text into sentences and sample the longest ones. For each sentence send GET /web with a quoted query, score every snippet by shared word triples, then fetch the best pages once with GET /web/extract?format=text and score the full sentence against the page text. Offer POST /search with page_text as an alternative engine. Show a plan and cost estimate before any paid call. Record X-Desearch-Cost-Usd per call. Write matches CSV, sentences CSV and a Markdown summary. Never give a verdict on authorship or AI writing.

    Build your own example.

    Give your agents access to web and social search. Get an API key to start building, or talk to us about your integration.

    Read docs