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 ConnectedCodequiry 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.
Open Source:
github.com/cqchecker/codequiry-go
📦 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
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()
}
}
}
⚡ 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))
}
}
🔧 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)
}