Coming soon We've been rebuilding our AI Code Detection so it's a lot better at catching ChatGPT and Claude, with an AI‑probability score on every check Learn more
Developer API

Code Plagiarism
Detection API

Build plagiarism detection into your apps. Check code against 20+ billion sources.

const response = await fetch('https://codequiry.com/api/v1/check', { method: 'POST', headers: { 'apikey': 'YOUR_API_KEY' }, body: formData }); // Check created!
GO SDK

Codequiry SDK for Go

The official Go SDK for Codequiry leverages Go's powerful concurrency features for high-performance plagiarism detection in microservices and cloud-native applications.

Installation

        # Go modules (recommended)
go mod init your-project
go get github.com/cqchecker/codequiry-go

# Legacy GOPATH
go get -u github.com/cqchecker/codequiry-go
        
Go Get
Requirements: Go 1.16 or higher

Quick Start

        package main

import (
    "context"
    "fmt"
    "log"
    "time"

    "github.com/cqchecker/codequiry-go"
)

func main() {
    // Initialize client
    client := codequiry.NewClient(&codequiry.Config{
        APIKey:     "your-api-key-here",
        BaseURL:    "https://codequiry.com/api/v1",
        Timeout:    30 * time.Second,
        MaxRetries: 3,
    })

    ctx := context.Background()

    // Test connection
    account, err := client.GetAccountInfo(ctx)
    if err != nil {
        log.Fatalf("Connection failed: %v", err)
    }

    fmt.Printf("Connected as: %s\n", account.Name)
    fmt.Printf("Checks remaining: %d\n", account.ChecksRemaining)

    // Run complete plagiarism check
    result, err := runPlagiarismCheck(ctx, client)
    if err != nil {
        log.Fatalf("Check failed: %v", err)
    }

    fmt.Printf("Check completed: %s\n", result.CheckID)
}

func runPlagiarismCheck(ctx context.Context, client *codequiry.Client) (*codequiry.CheckResult, error) {
    // 1. Create check
    checkReq := &codequiry.CheckRequest{
        Name:     "Go Assignment - Concurrent Programming",
        Language: 7, // Go language ID
    }

    check, err := client.CreateCheck(ctx, checkReq)
    if err != nil {
        return nil, fmt.Errorf("failed to create check: %w", err)
    }

    fmt.Printf("Created check: %s\n", check.ID)

    // 2. Upload files concurrently
    files := []string{
        "student1_submission.go",
        "student2_submission.go",
        "student3_submission.go",
    }

    err = uploadFilesAsync(ctx, client, check.ID, files)
    if err != nil {
        return nil, fmt.Errorf("failed to upload files: %w", err)
    }

    // 3. Start analysis
    analysisReq := &codequiry.AnalysisRequest{
        WebCheck:      true,
        DatabaseCheck: true,
        TestType:      1,
    }

    err = client.StartCheck(ctx, check.ID, analysisReq)
    if err != nil {
        return nil, fmt.Errorf("failed to start analysis: %w", err)
    }

    fmt.Println("Analysis started...")

    // 4. Wait for completion with progress
    overview, err := waitForCompletionWithProgress(ctx, client, check.ID)
    if err != nil {
        return nil, fmt.Errorf("analysis failed: %w", err)
    }

    fmt.Printf("Analysis complete! Found %d submissions\n", len(overview.Submissions))

    // 5. Process suspicious submissions
    for _, submission := range overview.Submissions {
        if submission.TotalResult > 50.0 {
            fmt.Printf("⚠️  %s: %.2f%% similarity\n", submission.Filename, submission.TotalResult)

            // Get detailed results
            details, err := client.GetDetailedResults(ctx, check.ID, submission.ID)
            if err != nil {
                log.Printf("Failed to get details for %s: %v", submission.Filename, err)
                continue
            }

            // Show peer matches
            for _, match := range details.PeerMatches {
                fmt.Printf("  → Match with %s: %.2f%%\n", match.FileMatched, match.Similarity)
            }
        }
    }

    return &codequiry.CheckResult{
        CheckID:  check.ID,
        Overview: overview,
    }, nil
}

func uploadFilesAsync(ctx context.Context, client *codequiry.Client, checkID string, files []string) error {
    // Create a buffered channel for upload results
    results := make(chan error, len(files))

    // Upload files concurrently
    for _, file := range files {
        go func(filename string) {
            _, err := client.UploadFile(ctx, checkID, filename)
            if err != nil {
                results <- fmt.Errorf("failed to upload %s: %w", filename, err)
            } else {
                fmt.Printf("Uploaded: %s\n", filename)
                results <- nil
            }
        }(file)
    }

    // Wait for all uploads to complete
    for i := 0; i < len(files); i++ {
        if err := <-results; err != nil {
            return err
        }
    }

    return nil
}

func waitForCompletionWithProgress(ctx context.Context, client *codequiry.Client, checkID string) (*codequiry.CheckOverview, error) {
    ticker := time.NewTicker(30 * time.Second)
    defer ticker.Stop()

    timeout := time.After(10 * time.Minute)

    for {
        select {
        case <-timeout:
            return nil, fmt.Errorf("analysis timed out after 10 minutes")

        case <-ticker.C:
            status, err := client.GetCheckStatus(ctx, checkID)
            if err != nil {
                return nil, fmt.Errorf("failed to get status: %w", err)
            }

            if status.StatusID == 4 { // Completed
                return client.GetOverview(ctx, checkID)
            }

            fmt.Printf("Status: %s\n", status.Message)

        case <-ctx.Done():
            return nil, ctx.Err()
        }
    }
}
        
Complete Example

Concurrent Processing

Leverage Go's goroutines for high-performance batch processing:

        package main

import (
    "context"
    "fmt"
    "sync"
    "time"

    "github.com/cqchecker/codequiry-go"
)

// BatchProcessor handles concurrent plagiarism checking
type BatchProcessor struct {
    client      *codequiry.Client
    maxWorkers  int
    semaphore   chan struct{}
}

func NewBatchProcessor(client *codequiry.Client, maxWorkers int) *BatchProcessor {
    return &BatchProcessor{
        client:     client,
        maxWorkers: maxWorkers,
        semaphore:  make(chan struct{}, maxWorkers),
    }
}

// Assignment represents a single assignment to check
type Assignment struct {
    Name       string
    Language   int
    Files      []string
    CheckWeb   bool
    CheckDB    bool
}

// Result represents the outcome of a plagiarism check
type Result struct {
    Assignment *Assignment
    CheckID    string
    Overview   *codequiry.CheckOverview
    Error      error
}

func (bp *BatchProcessor) ProcessAssignments(ctx context.Context, assignments []*Assignment) ([]*Result, error) {
    var wg sync.WaitGroup
    results := make(chan *Result, len(assignments))

    // Process each assignment concurrently
    for _, assignment := range assignments {
        wg.Add(1)

        go func(a *Assignment) {
            defer wg.Done()

            // Acquire semaphore slot
            bp.semaphore <- struct{}{}
            defer func() { <-bp.semaphore }()

            result := bp.processAssignment(ctx, a)
            results <- result
        }(assignment)
    }

    // Close results channel when all goroutines complete
    go func() {
        wg.Wait()
        close(results)
    }()

    // Collect results
    var allResults []*Result
    for result := range results {
        allResults = append(allResults, result)
    }

    return allResults, nil
}

func (bp *BatchProcessor) processAssignment(ctx context.Context, assignment *Assignment) *Result {
    result := &Result{Assignment: assignment}

    // Create check
    checkReq := &codequiry.CheckRequest{
        Name:     assignment.Name,
        Language: assignment.Language,
    }

    check, err := bp.client.CreateCheck(ctx, checkReq)
    if err != nil {
        result.Error = fmt.Errorf("failed to create check: %w", err)
        return result
    }

    result.CheckID = check.ID

    // Upload files with worker pool
    err = bp.uploadFilesWithPool(ctx, check.ID, assignment.Files)
    if err != nil {
        result.Error = fmt.Errorf("failed to upload files: %w", err)
        return result
    }

    // Start analysis
    analysisReq := &codequiry.AnalysisRequest{
        WebCheck:      assignment.CheckWeb,
        DatabaseCheck: assignment.CheckDB,
    }

    err = bp.client.StartCheck(ctx, check.ID, analysisReq)
    if err != nil {
        result.Error = fmt.Errorf("failed to start analysis: %w", err)
        return result
    }

    // Wait for completion
    overview, err := bp.waitForCompletion(ctx, check.ID)
    if err != nil {
        result.Error = fmt.Errorf("analysis failed: %w", err)
        return result
    }

    result.Overview = overview
    return result
}

func (bp *BatchProcessor) uploadFilesWithPool(ctx context.Context, checkID string, files []string) error {
    var wg sync.WaitGroup
    errChan := make(chan error, len(files))

    for _, file := range files {
        wg.Add(1)

        go func(filename string) {
            defer wg.Done()

            _, err := bp.client.UploadFile(ctx, checkID, filename)
            if err != nil {
                errChan <- fmt.Errorf("failed to upload %s: %w", filename, err)
            }
        }(file)
    }

    // Wait for all uploads
    go func() {
        wg.Wait()
        close(errChan)
    }()

    // Check for errors
    for err := range errChan {
        if err != nil {
            return err
        }
    }

    return nil
}

func (bp *BatchProcessor) waitForCompletion(ctx context.Context, checkID string) (*codequiry.CheckOverview, error) {
    timeout := time.After(10 * time.Minute)
    ticker := time.NewTicker(30 * time.Second)
    defer ticker.Stop()

    for {
        select {
        case <-timeout:
            return nil, fmt.Errorf("analysis timed out")

        case <-ticker.C:
            status, err := bp.client.GetCheckStatus(ctx, checkID)
            if err != nil {
                return nil, fmt.Errorf("failed to get status: %w", err)
            }

            if status.StatusID == 4 { // Completed
                return bp.client.GetOverview(ctx, checkID)
            }

        case <-ctx.Done():
            return nil, ctx.Err()
        }
    }
}

// Usage example
func main() {
    client := codequiry.NewClient(&codequiry.Config{
        APIKey:     "your-api-key-here",
        Timeout:    30 * time.Second,
        MaxRetries: 3,
    })

    processor := NewBatchProcessor(client, 5) // Max 5 concurrent checks

    assignments := []*Assignment{
        {
            Name:     "Assignment 1 - Algorithms",
            Language: 7, // Go
            Files:    []string{"student1.go", "student2.go"},
            CheckWeb: true,
            CheckDB:  true,
        },
        {
            Name:     "Assignment 2 - Data Structures",
            Language: 7,
            Files:    []string{"student3.go", "student4.go"},
            CheckWeb: true,
            CheckDB:  true,
        },
    }

    ctx := context.WithTimeout(context.Background(), 30*time.Minute)

    results, err := processor.ProcessAssignments(ctx, assignments)
    if err != nil {
        log.Fatalf("Batch processing failed: %v", err)
    }

    // Process results
    for _, result := range results {
        if result.Error != nil {
            fmt.Printf("❌ %s failed: %v\n", result.Assignment.Name, result.Error)
            continue
        }

        fmt.Printf("✅ %s completed: %d submissions\n",
            result.Assignment.Name, len(result.Overview.Submissions))
    }
}
        
Concurrent Processing

HTTP Server Integration

        package main

import (
    "context"
    "encoding/json"
    "fmt"
    "net/http"
    "strconv"
    "time"

    "github.com/gorilla/mux"
    "github.com/cqchecker/codequiry-go"
)

type PlagiarismServer struct {
    client *codequiry.Client
    checks map[string]*CheckInfo // In-memory store (use Redis in production)
}

type CheckInfo struct {
    CheckID   string                   `json:"check_id"`
    Status    string                   `json:"status"`
    Overview  *codequiry.CheckOverview `json:"overview,omitempty"`
    Error     string                   `json:"error,omitempty"`
    CreatedAt time.Time               `json:"created_at"`
}

func NewPlagiarismServer() *PlagiarismServer {
    client := codequiry.NewClient(&codequiry.Config{
        APIKey:  getEnvOrDefault("CODEQUIRY_API_KEY", ""),
        Timeout: 30 * time.Second,
    })

    return &PlagiarismServer{
        client: client,
        checks: make(map[string]*CheckInfo),
    }
}

func (ps *PlagiarismServer) handleUpload(w http.ResponseWriter, r *http.Request) {
    err := r.ParseMultipartForm(32 << 20) // 32 MB
    if err != nil {
        http.Error(w, "Failed to parse form", http.StatusBadRequest)
        return
    }

    name := r.FormValue("name")
    languageStr := r.FormValue("language")

    language, err := strconv.Atoi(languageStr)
    if err != nil {
        http.Error(w, "Invalid language ID", http.StatusBadRequest)
        return
    }

    files := r.MultipartForm.File["files"]
    if len(files) == 0 {
        http.Error(w, "No files provided", http.StatusBadRequest)
        return
    }

    ctx := context.Background()

    // Create check
    checkReq := &codequiry.CheckRequest{
        Name:     name,
        Language: language,
    }

    check, err := ps.client.CreateCheck(ctx, checkReq)
    if err != nil {
        http.Error(w, fmt.Sprintf("Failed to create check: %v", err), http.StatusInternalServerError)
        return
    }

    // Store check info
    checkInfo := &CheckInfo{
        CheckID:   check.ID,
        Status:    "uploading",
        CreatedAt: time.Now(),
    }
    ps.checks[check.ID] = checkInfo

    // Process uploads asynchronously
    go ps.processUploadsAsync(ctx, check.ID, files)

    response := map[string]interface{}{
        "success":  true,
        "check_id": check.ID,
        "message":  "Files uploaded successfully. Analysis started.",
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(response)
}

func (ps *PlagiarismServer) processUploadsAsync(ctx context.Context, checkID string, files []*multipart.FileHeader) {
    checkInfo := ps.checks[checkID]

    // Upload files
    var uploadedCount int
    for _, fileHeader := range files {
        file, err := fileHeader.Open()
        if err != nil {
            checkInfo.Error = fmt.Sprintf("Failed to open file %s: %v", fileHeader.Filename, err)
            checkInfo.Status = "failed"
            return
        }
        defer file.Close()

        _, err = ps.client.UploadFileFromReader(ctx, checkID, file, fileHeader.Filename)
        if err != nil {
            checkInfo.Error = fmt.Sprintf("Failed to upload file %s: %v", fileHeader.Filename, err)
            checkInfo.Status = "failed"
            return
        }

        uploadedCount++
    }

    if uploadedCount == 0 {
        checkInfo.Error = "No files were successfully uploaded"
        checkInfo.Status = "failed"
        return
    }

    // Start analysis
    analysisReq := &codequiry.AnalysisRequest{
        WebCheck:      true,
        DatabaseCheck: true,
    }

    err := ps.client.StartCheck(ctx, checkID, analysisReq)
    if err != nil {
        checkInfo.Error = fmt.Sprintf("Failed to start analysis: %v", err)
        checkInfo.Status = "failed"
        return
    }

    checkInfo.Status = "processing"

    // Wait for completion
    go ps.waitForCompletionAsync(ctx, checkID)
}

func (ps *PlagiarismServer) waitForCompletionAsync(ctx context.Context, checkID string) {
    checkInfo := ps.checks[checkID]

    ticker := time.NewTicker(30 * time.Second)
    defer ticker.Stop()

    timeout := time.After(15 * time.Minute)

    for {
        select {
        case <-timeout:
            checkInfo.Error = "Analysis timed out"
            checkInfo.Status = "timeout"
            return

        case <-ticker.C:
            status, err := ps.client.GetCheckStatus(ctx, checkID)
            if err != nil {
                checkInfo.Error = fmt.Sprintf("Failed to get status: %v", err)
                checkInfo.Status = "failed"
                return
            }

            if status.StatusID == 4 { // Completed
                overview, err := ps.client.GetOverview(ctx, checkID)
                if err != nil {
                    checkInfo.Error = fmt.Sprintf("Failed to get overview: %v", err)
                    checkInfo.Status = "failed"
                    return
                }

                checkInfo.Overview = overview
                checkInfo.Status = "completed"
                return
            }
        }
    }
}

func (ps *PlagiarismServer) handleStatus(w http.ResponseWriter, r *http.Request) {
    vars := mux.Vars(r)
    checkID := vars["checkId"]

    checkInfo, exists := ps.checks[checkID]
    if !exists {
        http.Error(w, "Check not found", http.StatusNotFound)
        return
    }

    w.Header().Set("Content-Type", "application/json")
    json.NewEncoder(w).Encode(checkInfo)
}

func main() {
    server := NewPlagiarismServer()

    r := mux.NewRouter()
    r.HandleFunc("/upload", server.handleUpload).Methods("POST")
    r.HandleFunc("/status/{checkId}", server.handleStatus).Methods("GET")

    fmt.Println("Plagiarism detection server running on :8080")
    http.ListenAndServe(":8080", r)
}
        
HTTP Server