Developer API

Code Plagiarism
Detection API

Build plagiarism detection into your applications with our powerful REST API. Detect copied code across 20+ billion sources with industry-leading accuracy.

99.7%
Accuracy
51+
Languages
20B+
Sources
5
SDKs

API Playground

Not Connected

Connect your API key to test endpoints

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