# Detect Plagiarism in Code

> Scan source code files with the Copyleaks Plagiarism Checker API to find out whether your code appears on the web, in the Shared Data Hub, or in your own repositories.

The Copyleaks Authenticity API scans source code the same way it scans text. You submit a code file to the same [Submit File endpoint](/reference/actions/authenticity/submit-file) you already use for documents, Copyleaks compares it against billions of online sources, and your server is notified via webhooks when the results are ready.

There is no separate endpoint, plan, or feature flag for code. If you can scan a `.txt` file, you can scan a `.py` file. The only difference is the file extension you send.

Teams use code scans to:
- Confirm that proprietary code has not appeared in public repositories, forums, or documentation sites.
- Check that contributed or outsourced code was not copied from other sources, to stay on the right side of licensing agreements.
- Review programming assignments for academic integrity.

This guide walks you through submitting a code file, tuning the scan for source code, and exporting the results.

## Get started

<Steps>
  <Step title="Before you begin">
    Before you start, ensure you have the following:
    - An active Copyleaks account. If you don't have one, **[sign up for free](https://api.copyleaks.com/signup)**.
    - You can find your API key on the **[API Dashboard](https://api.copyleaks.com/dashboard)**.
  </Step>

  <Step title="Installation">
    <InstallSDKs />
  </Step>

  <Step title="Login">
    <GuideLogin />
  </Step>

  <Step title="Submit the code file for scanning">
    Use the [Submit File Endpoint](/reference/actions/authenticity/submit-file) to send the file for analysis. The request is identical to a text submission: the file content goes in `base64`, the file name goes in `filename`, and the scan settings go in `properties`. We suggest you provide a unique `scanId` for each submission, for example one derived from the file path.

    <Note title="The file extension is what makes it a code scan">
    The extension in `filename` tells Copyleaks how to read the file, so use the real extension of your source file (for example `main.py`, `App.java`, or `server.js`). Supported source code extensions:

    `ts`, `py`, `go`, `cs`, `c`, `h`, `idc`, `cpp`, `hpp`, `c++`, `h++`, `cc`, `hh`, `java`, `js`, `swift`, `rb`, `pl`, `php`, `sh`, `m`, `scala`, `css`

    Source code files can be up to 3 MB each. See the full [technical specifications](/reference/data-types/authenticity/technical-specifications) for all input limits.
    </Note>

    <Tip>
    For testing, set `"sandbox": true`. Sandbox mode is free and returns mock results.
    </Tip>

    <Note>
    **What is Base64 Encoding?**

    Base64 converts binary files into text strings so they can be sent via JSON. All programming languages have built-in Base64 encoding functions, see the code examples below for your language.
    </Note>

    <Warning>
    AI Content Detection is not supported for source code files, so keep `aiGeneratedText.detect` at its default (`false`) and don't expect an AI detection result for code. Plagiarism detection is not affected. See the [release notes](/resources/updates/release-notes) from December 16, 2025 for details.
    </Warning>

    <CodeGroup>
        ```http title="HTTP" icon="globe"
        PUT https://api.copyleaks.com/v3/scans/submit/file/my-code-scan

        Headers
        Authorization: Bearer <YOUR_AUTH_TOKEN>
        Content-Type: application/json

        Body
        {
            "base64": "ZGVmIGFkZChhLCBiKToKICAgIHJldHVybiBhICsgYgo=",
            "filename": "main.py",
            "properties": {
                "webhooks": {
                    "status": "https://your-server.com/webhook/{STATUS}"
                },
                "sandbox": true,
                "sensitivityLevel": 3,
                "scanning": {
                    "internet": true,
                    "copyleaksDb": {
                        "includeMySubmissions": true,
                        "includeOthersSubmissions": true
                    }
                },
                "filters": {
                    "minCopiedWords": 10
                }
            }
        }
        ```
      ```bash title="cURL" icon="terminal"
      # Encode the source file. Works on macOS and Linux.
      BASE64_CONTENT=$(base64 < main.py | tr -d '\n')

      curl -X PUT "https://api.copyleaks.com/v3/scans/submit/file/my-code-scan" \
           -H "Authorization: Bearer <YOUR_AUTH_TOKEN>" \
           -H "Content-Type: application/json" \
           -d "{
                 \"base64\": \"${BASE64_CONTENT}\",
                 \"filename\": \"main.py\",
                 \"properties\": {
                   \"webhooks\": {
                     \"status\": \"https://your-server.com/webhook/{STATUS}\"
                   },
                   \"sandbox\": true,
                   \"sensitivityLevel\": 3,
                   \"scanning\": {
                     \"internet\": true,
                     \"copyleaksDb\": {
                       \"includeMySubmissions\": true,
                       \"includeOthersSubmissions\": true
                     }
                   },
                   \"filters\": {
                     \"minCopiedWords\": 10
                   }
                 }
               }"
      ```
      ```python title="Python" icon="python"
      import base64
      from copyleaks.copyleaks import Copyleaks
      from copyleaks.models.submit.document import FileDocument
      from copyleaks.models.submit.properties.scan_properties import ScanProperties
      from copyleaks.models.submit.properties.submit_webhooks import SubmitWebhooks

      # Read the source file and encode it as Base64. Keep the real extension in the filename.
      FILENAME = "main.py"
      with open(FILENAME, "rb") as f:
          base64_content = base64.b64encode(f.read()).decode("utf-8")

      # A scan ID you can map back to the file, for example a path-based one.
      scan_id = "my-code-scan"
      file_submission = FileDocument(base64_content, FILENAME)

      # Copyleaks calls this webhook when the scan completes. The endpoint must be publicly reachable.
      webhooks = SubmitWebhooks()
      webhooks.set_status('https://your-server.com/webhook/{STATUS}')
      webhooks.set_new_result('https://your-server.com/webhook/new-results')

      scan_properties = ScanProperties(status_webhook='https://your-server.com/webhook/{STATUS}')
      scan_properties.set_webhooks(webhooks)
      scan_properties.set_sandbox(True)  # Turn on sandbox mode. Turn off on production.
      file_submission.set_properties(scan_properties)

      Copyleaks.submit_file(auth_token, scan_id, file_submission)
      print("Sent to scanning")
      print("You will be notified, using your webhook, once the scan is completed.")
      ```
      ```javascript title="JavaScript" icon="square-js"
      const { Copyleaks, CopyleaksFileSubmissionModel } = require('plagiarism-checker');
      const fs = require('fs');

      async function submitCodeForPlagiarismCheck() {
          try {
              // Initialize Copyleaks
              const copyleaks = new Copyleaks();

              // Login to get the authentication token.
              // Replace with your email and API key.
              const loginResult = await copyleaks.loginAsync('YOUR_EMAIL@example.com', 'YOUR_API_KEY');

              const scanId = `code-plagiarism-scan-${Date.now()}`;
              const WEBHOOK_URL = "https://your-server.com/webhook";

              // Read the source file and encode it as Base64. Keep the real extension in the filename.
              const filename = 'main.py';
              const base64Content = fs.readFileSync(filename).toString('base64');

              // Create a submission model
              const submission = new CopyleaksFileSubmissionModel(
                  base64Content,
                  filename,
                  {
                      sandbox: true, // Use sandbox for testing
                      webhooks: {
                          // Copyleaks will notify this URL when the scan is complete.
                          status: `${WEBHOOK_URL}/{STATUS}`
                      },
                      sensitivityLevel: 3,
                      scanning: {
                          internet: true,
                          copyleaksDb: {
                              includeMySubmissions: true,
                              includeOthersSubmissions: true
                          }
                      },
                      filters: {
                          // Skip very short matches such as import lines and license headers
                          minCopiedWords: 10
                      }
                  }
              );

              // Submit the file for scanning
              await copyleaks.submitFileAsync(loginResult, scanId, submission);
              console.log(`Submission successful. Scan ID: ${scanId}`);

          } catch (error) {
              console.error("An error occurred:", error);
          }
      }

      submitCodeForPlagiarismCheck();
      ```
      ```java title="Java" icon="java"
      import classes.Copyleaks;
      import models.submissions.CopyleaksFileSubmissionModel;
      import models.submissions.properties.*;
      import java.util.Base64;
      import java.nio.file.*;

      String scanId = "my-code-scan";

      // Read the source file and encode it as Base64. Keep the real extension in the filename.
      String filename = "App.java";
      String base64Content = Base64.getEncoder().encodeToString(Files.readAllBytes(Paths.get(filename)));

      // Configure webhooks
      SubmissionWebhooks webhooks = new SubmissionWebhooks("https://your-server.com/webhook/{STATUS}");
      webhooks.setNewResult("https://your-server.com/webhook/new-results");

      // Create submission properties
      SubmissionProperties properties = new SubmissionProperties(webhooks);
      properties.setSandbox(true); // Turn on sandbox mode. Turn off on production.

      // Compare against online sources
      SubmissionScanning scanning = new SubmissionScanning();
      scanning.setInternet(true);
      properties.setScanning(scanning);

      // Optional: 1 (faster) to 5 (slower, more thorough)
      properties.setSensitivityLevel(3);

      // Create and submit the file
      CopyleaksFileSubmissionModel submission = new CopyleaksFileSubmissionModel(base64Content, filename, properties);

      Copyleaks.submitFile(authToken, scanId, submission);
      System.out.println("Sent to scanning...");
      ```
    </CodeGroup>
  </Step>

  <Step title="Wait for the completion webhook">
    The scan can take some time. Once it's complete, Copyleaks sends a [completed webhook](/reference/data-types/authenticity/webhooks/scan-completed) to the status URL you provided. It contains a summary of the scan: an overall score, and one entry per source where the code was found, each with a `result` ID you will use in the next step.

    Matches are grouped by where they came from:
    - `results.internet` - public web pages, including code hosting sites, forums, and documentation.
    - `results.database` - documents in the Copyleaks Shared Data Hub.
    - `results.repositories` - your own [Private Cloud Hub](/concepts/features/data-hubs) repositories, if you scanned against any.

    A trimmed example for a code file that was found on a public repository:

    ```json title="Completed webhook (trimmed)"
    {
      "status": 0,
      "scannedDocument": {
        "scanId": "my-code-scan",
        "totalWords": 412,
        "totalExcluded": 0,
        "credits": 2,
        "creationTime": "2026-09-03T10:14:52.118Z",
        "metadata": {
          "filename": "main.py"
        }
      },
      "results": {
        "internet": [
          {
            "id": "3f8a1c2d9e",
            "title": "utils/main.py at master - example/repo",
            "introduction": "def add(a, b): return a + b ...",
            "matchedWords": 96,
            "url": "https://github.com/example/repo/blob/master/utils/main.py"
          }
        ],
        "database": [],
        "batch": [],
        "repositories": [],
        "score": {
          "identicalWords": 96,
          "minorChangedWords": 0,
          "relatedMeaningWords": 0,
          "aggregatedScore": 23.3
        }
      },
      "notifications": {
        "alerts": []
      }
    }
    ```

    An empty `results` section with an `aggregatedScore` of `0` means none of the code was found in the sources you scanned against.
  </Step>

  <Step title="Export detailed results">
    After the `completed` webhook arrives, use the [export endpoint](/reference/actions/downloads/export) to retrieve the detailed plagiarism [`results`](/reference/data-types/authenticity/results/new-plagiarism-result) using the `result` IDs you received in the completion webhook. Each result contains the character positions of every matched range in your file and in the source, which you can map back to line numbers or feed into the [Copyleaks report](/concepts/features/how-to-display).

    We will also export the Crawled Version. The `crawledVersion` webhook contains the text version of the submitted file as Copyleaks processed it. This can later be used in order to display the report.

    In addition, you should also specify a `completionWebhook` to receive notifications when the export is ready.

    <CodeGroup>
        ```http title="HTTP" icon="globe"
        POST https://api.copyleaks.com/v3/downloads/my-code-scan/export/<export_id>

        Headers
        Authorization: Bearer <your_token>
        Content-Type: application/json

        Body
        {
            "completionWebhook":  "https://your-server.com/webhook/export/completion",
            "maxRetries": 3,
            "developerPayload": "custom_data_identifier",
            "crawledVersion": {
                "endpoint": "https://your-server.com/webhook/export/crawled",
                "verb": "POST",
                "headers": [
                    [
                        "header-key",
                        "header-value"
                    ]
                ]
            },
            "results": [
                {
                    "id": "3f8a1c2d9e",
                    "endpoint": "https://your-server.com/webhook/export/result/3f8a1c2d9e",
                    "verb": "POST",
                    "headers": [
                        [
                            "header-key",
                            "header-value"
                        ]
                    ]
                }
            ]
        }
        ```
      ```bash title="cURL" icon="terminal"
      curl -X POST "https://api.copyleaks.com/v3/downloads/my-code-scan/export/my-export-1" \
          -H "Authorization: Bearer <YOUR_AUTH_TOKEN>" \
          -H "Content-Type: application/json" \
          -d '{
                "completionWebhook": "https://your-server.com/webhook/export/completion",
                "maxRetries": 3,
                "developerPayload": "custom_data_identifier",
                "crawledVersion": {
                    "endpoint": "https://your-server.com/webhook/export/crawled",
                    "verb": "POST",
                    "headers": {
                        "header-key": "header-value"
                    }
                },
                "results": [
                    {
                        "id": "<RESULT_ID_FROM_COMPLETED_WEBHOOK>",
                        "endpoint": "https://your-server.com/webhook/export/result/1",
                        "verb": "POST",
                        "headers": {
                            "header-key": "header-value"
                        }
                    }
                ]
              }'
      ```
      ```python title="Python" icon="python"
      from copyleaks.copyleaks import Copyleaks
      from copyleaks.models.export import Export, ExportResult

      export_id = "my-export-1"
      export = Export()
      export.set_completion_webhook('https://your-server.com/webhook/export/completion')

      # Export a specific plagiarism result
      result1 = ExportResult()
      result1.set_id('<RESULT_ID_FROM_COMPLETED_WEBHOOK>')
      result1.set_endpoint('https://your-server.com/webhook/export/result/1')  # Only URL
      result1.set_verb('POST')  # HTTP method separately
      export.set_results([result1])

      Copyleaks.export(auth_token, scan_id, export_id, export)
      print("Export initiated.")
      ```
      ```javascript title="JavaScript" icon="square-js"
        const { Copyleaks, CopyleaksExportModel } = require('plagiarism-checker');

        async function exportPlagiarismResults() {
            try {
                // Initialize Copyleaks
                const copyleaks = new Copyleaks();

                // Login to get the authentication token.
                // Replace with your email and API key.
                const loginResult = await copyleaks.loginAsync('YOUR_EMAIL@example.com', 'YOUR_API_KEY');

                const scanId = "my-code-scan"; // The ID of the scan you want to export
                const exportId = `export-${scanId}-${Date.now()}`;
                const WEBHOOK_URL = "https://your-server.com/webhook";

                // The result IDs to export, obtained from the completion webhook
                const resultIdsToExport = ["RESULT_ID_1", "RESULT_ID_2"];

                const results = resultIdsToExport.map(resultId => ({
                    id: resultId,
                    endpoint: `${WEBHOOK_URL}/export/${exportId}/result/${resultId}`,
                    verb: "POST"
                }));

                // Create an export model
                const exportModel = new CopyleaksExportModel(
                    `${WEBHOOK_URL}/export/${exportId}/completion`, // Completion webhook URL
                    results,
                    {
                        // Request to export the crawled version of the submitted file
                        endpoint: `${WEBHOOK_URL}/export/${exportId}/crawled-version`,
                        verb: "POST"
                    }
                );

                // Start the export process
                await copyleaks.exportAsync(loginResult, scanId, exportId, exportModel);
                console.log(`Export initiated. Export ID: ${exportId}`);

            } catch (error) {
                console.error("An error occurred:", error);
            }
        }

        exportPlagiarismResults();
      ```
        ```java title="Java" icon="java"
            import classes.Copyleaks;
            import models.exports.*;

            String scanId = "my-code-scan"; // Your scan ID from submission
            String exportId = "my-export-1"; // Your chosen export ID

            // Optional headers Copyleaks will send with each export webhook
            String[][] headers = new String[][]{
                new String[]{"header-key", "header-value"}
            };

            // Export a specific plagiarism result
            ExportResults results = new ExportResults(
                "3f8a1c2d9e", // Result ID from the completed webhook
                "https://your-server.com/webhook/export/result/3f8a1c2d9e", // Endpoint URL
                "POST", // HTTP method
                headers // Optional headers
            );

            ExportResults[] exportResultsArray = new ExportResults[]{ results };

            // Export the crawled version of the submitted file
            ExportCrawledVersion crawledVersion = new ExportCrawledVersion(
                "https://your-server.com/webhook/export/crawled",
                "POST",
                headers
            );

            // Create the export model
            CopyleaksExportModel exportModel = new CopyleaksExportModel(
                "https://your-server.com/webhook/export/completion", // Completion webhook
                exportResultsArray, // Results to export
                crawledVersion // Crawled version
            );

            try {
                Copyleaks.export(token, scanId, exportId, exportModel);
                System.out.println("Export initiated successfully.");
            } catch (Exception e) {
                System.out.println("Export failed: " + e.getMessage());
                e.printStackTrace();
            }
        ```
    </CodeGroup>
  </Step>

  <Step title="Summary">
    You have successfully submitted a source code file for plagiarism detection and exported the results. You can now handle the results in your application, highlight the matched lines for your users, or take further actions based on the findings.
  </Step>
</Steps>

## Tune the scan for source code

The defaults work for most code scans. These settings are the ones teams most often adjust:

- **Where to look.** `scanning.internet` (on by default) covers public web sources. Add `scanning.repositories` to also compare against your own codebase in a [Private Cloud Hub](/concepts/features/data-hubs), which is how you check new code against everything your organization already indexed.
- **Keep proprietary code private.** By default, `indexing.copyleaksDb` is `false`, so your file is not added to the Shared Data Hub and other Copyleaks users can never match against it. Leave it that way for proprietary code. If you don't want the file kept for comparison against your own future submissions either, set `scanning.copyleaksDb.includeMySubmissions` to `false`.
- **Cut down on noise.** Source files share a lot of boilerplate, such as import blocks, license headers, and common idioms. Use `filters.minCopiedWords` to hide results below a minimum match size, and `sensitivityLevel` (1 is fastest, 5 is most thorough) to control how deep the scan goes. See [Detection Levels](/concepts/features/detection-levels) for how identical, minor-change, and paraphrase matching can be toggled individually.
- **Ignore sources you own.** If your own public sites show up as matches, exclude them with `filters.domains` together with `filters.domainsMode` set to `1`.

All properties are documented on the [Submit File reference](/reference/actions/authenticity/submit-file).

## Frequently asked questions

<AccordionGroup>
  <Accordion title="Do I need a separate endpoint or plan to scan source code?">
    No. Source code goes through the same [Submit File endpoint](/reference/actions/authenticity/submit-file) as text and documents, uses the same properties, and is billed with the same plagiarism credits. The file extension in `filename` is all that tells Copyleaks it is code.
  </Accordion>
  <Accordion title="Which programming languages are supported?">
    Files with these extensions are accepted: `ts`, `py`, `go`, `cs`, `c`, `h`, `idc`, `cpp`, `hpp`, `c++`, `h++`, `cc`, `hh`, `java`, `js`, `swift`, `rb`, `pl`, `php`, `sh`, `m`, `scala`, `css`. The full list is in the [technical specifications](/reference/data-types/authenticity/technical-specifications).
  </Accordion>
  <Accordion title="Can I scan a whole repository at once?">
    Each submission is one file, and archives such as `.zip` are not supported. To scan a repository, loop over the source files and submit each one with its own `scanId`. A path-based ID such as `repo-src-utils-main-py` makes it easy to map results back to files.
  </Accordion>
  <Accordion title="Can I check code against my own private codebase?">
    Yes. Index your existing files into a [Private Cloud Hub](/concepts/features/data-hubs) repository, then scan new files against it with `scanning.repositories`. Matches from your repository come back in `results.repositories` in the completed webhook.
  </Accordion>
  <Accordion title="Will my code be shared with other Copyleaks users?">
    Only if you set `indexing.copyleaksDb` to `true`, which adds the file to the Shared Data Hub. It is `false` by default, so a normal scan does not share your code with anyone.
  </Accordion>
  <Accordion title="Can I detect AI-generated code?">
    No. AI Content Detection for source code was deprecated on December 16, 2025 and is no longer supported on the submit endpoints. Plagiarism detection for code is unaffected. See the [release notes](/resources/updates/release-notes) for details.
  </Accordion>
  <Accordion title="How are credits counted for code files?">
    The same way as for text: every 250 words, or part of that, counts as one page. See [technical specifications](/reference/data-types/authenticity/technical-specifications) for the full breakdown.
  </Accordion>
  <Accordion title="Can I test code scanning without using credits?">
    Yes. Set `"sandbox": true` in the submission properties. Sandbox mode is free and returns mock results, so you can wire up the flow before going live.
  </Accordion>
</AccordionGroup>

## Next steps
<CardGroup cols={2}>
    <Card title="Webhooks Overview" icon="plug" href="/reference/data-types/authenticity/webhooks/overview/">Learn how to securely receive and process notifications from Copyleaks.</Card>
    <Card title="Viewing Scan Results" icon="browser" href="/concepts/features/how-to-display/">Understand the scan result format and how to display it to your users.</Card>
    <Card title="Data Hubs" icon="database" href="/concepts/features/data-hubs">Compare code against your own private repositories or the Shared Data Hub.</Card>
    <Card title="Submit File Reference" icon="code" href="/reference/actions/authenticity/submit-file">Every property you can set on a scan, with request and response examples.</Card>
</CardGroup>
