Skip to content

Submissions API ​

Endpoints for submitting solutions and viewing submission history.

Submit Solution ​

Submit code to be tested against all test cases. On success, updates user progress for spaced repetition.

Endpoint ​

http
POST /api/submit

Authentication ​

Required - Include Authorization header

Request Body ​

json
{
  "problem_slug": "two-sum",
  "language": "python",
  "code": "class Solution:\n    def twoSum(self, nums, target):\n        seen = {}\n        for i, num in enumerate(nums):\n            complement = target - num\n            if complement in seen:\n                return [seen[complement], i]\n            seen[num] = i\n        return []"
}

Response (All Tests Pass) ​

json
{
  "success": true,
  "results": [
    {
      "test_number": 1,
      "passed": true,
      "input": [[2, 7, 11, 15], 9],
      "output": [0, 1],
      "expected": [0, 1],
      "stdout": null,
      "error": null
    },
    {
      "test_number": 2,
      "passed": true,
      "input": [[3, 2, 4], 6],
      "output": [1, 2],
      "expected": [1, 2],
      "stdout": null,
      "error": null
    },
    {
      "test_number": 3,
      "passed": true,
      "input": [[3, 3], 6],
      "output": [0, 1],
      "expected": [0, 1],
      "stdout": null,
      "error": null
    },
    {
      "test_number": 4,
      "passed": true,
      "input": [[1, 5, 3, 7, 2, 8], 10],
      "output": [2, 4],
      "expected": [2, 4],
      "stdout": null,
      "error": null
    },
    {
      "test_number": 5,
      "passed": true,
      "input": [[0, 4, 3, 0], 0],
      "output": [0, 3],
      "expected": [0, 3],
      "stdout": null,
      "error": null
    }
  ],
  "summary": {
    "total": 5,
    "passed": 5,
    "failed": 0
  },
  "total_test_cases": 5,
  "submission_id": "uuid",
  "runtime_ms": 45,
  "memory_kb": 14320,
  "compile_error": null,
  "runtime_error": null,
  "times_solved": 1,
  "is_mastered": false,
  "next_review_date": "2024-01-15"
}

Response (Test Fails - Early Exit) ​

When a test fails, execution stops (early exit) to save resources:

json
{
  "success": false,
  "results": [
    {
      "test_number": 1,
      "passed": true,
      "input": [[2, 7, 11, 15], 9],
      "output": [0, 1],
      "expected": [0, 1],
      "stdout": null,
      "error": null
    },
    {
      "test_number": 2,
      "passed": true,
      "input": [[3, 2, 4], 6],
      "output": [1, 2],
      "expected": [1, 2],
      "stdout": null,
      "error": null
    },
    {
      "test_number": 3,
      "passed": false,
      "input": [[3, 3], 6],
      "output": [1, 0],
      "expected": [0, 1],
      "stdout": "Debug: checking index 1\n",
      "error": null
    }
  ],
  "summary": {
    "total": 3,
    "passed": 2,
    "failed": 1
  },
  "total_test_cases": 5,
  "submission_id": "uuid",
  "runtime_ms": 12,
  "memory_kb": 14100,
  "compile_error": null,
  "runtime_error": null,
  "times_solved": null,
  "is_mastered": null,
  "next_review_date": null
}

Note: summary.total shows tests executed (3), while total_test_cases shows total tests for the problem (5). Use total_test_cases for "X / Y passed" display.

Response (Compile Error) ​

json
{
  "success": false,
  "results": [],
  "summary": {
    "total": 0,
    "passed": 0,
    "failed": 0
  },
  "total_test_cases": 5,
  "submission_id": "uuid",
  "runtime_ms": 0,
  "memory_kb": 0,
  "compile_error": "SyntaxError: invalid syntax (line 3)",
  "runtime_error": null,
  "times_solved": null,
  "is_mastered": null,
  "next_review_date": null
}

Response (Runtime Error) ​

json
{
  "success": false,
  "results": [
    {
      "test_number": 1,
      "passed": false,
      "input": [[2, 7, 11, 15], 9],
      "output": null,
      "expected": [0, 1],
      "stdout": null,
      "error": "IndexError: list index out of range"
    }
  ],
  "summary": {
    "total": 1,
    "passed": 0,
    "failed": 1
  },
  "total_test_cases": 5,
  "submission_id": "uuid",
  "runtime_ms": 5,
  "memory_kb": 13000,
  "compile_error": null,
  "runtime_error": "IndexError: list index out of range",
  "times_solved": null,
  "is_mastered": null,
  "next_review_date": null
}

Progress Updates (Anki SM-2) ​

When all tests pass, the submission is recorded but progress is NOT automatically updated. You must call the rate-submission endpoint to update progress with your difficulty rating.

See Rate Submission below.

Example ​

bash
curl -X POST http://localhost:8000/api/submit \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "problem_slug": "two-sum",
    "language": "python",
    "code": "class Solution:\n    def twoSum(self, nums, target):\n        seen = {}\n        for i, num in enumerate(nums):\n            if target - num in seen:\n                return [seen[target - num], i]\n            seen[num] = i"
  }'

Rate Submission ​

After a successful submission, rate the difficulty to update your spaced repetition progress (Anki SM-2).

Endpoint ​

http
POST /api/rate-submission

Authentication ​

Required - Include Authorization header

Request Body ​

json
{
  "submission_id": "uuid",
  "rating": "good"
}

Rating options:

RatingDescriptionEffect
againCouldn't solve / needed hintsReset interval to 1 day, decrease ease
hardSolved but struggledSlower growth (×1.2), decrease ease
goodSolved with some effortNormal growth (×ease factor)
masteredSolved easily, confidentExit rotation immediately

Response ​

json
{
  "success": true,
  "progress": {
    "times_solved": 2,
    "ease_factor": 2.5,
    "interval_days": 8,
    "is_mastered": false,
    "next_review_date": "2024-01-15"
  }
}

Example ​

bash
curl -X POST http://localhost:8000/api/rate-submission \
  -H "Authorization: Bearer <token>" \
  -H "Content-Type: application/json" \
  -d '{
    "submission_id": "abc123-uuid",
    "rating": "good"
  }'

Get Submission History ​

Retrieve past submissions for a specific problem.

Endpoint ​

http
GET /api/submissions/:problem_id

Authentication ​

Required - Include Authorization header

Path Parameters ​

ParameterTypeDescription
problem_idUUIDProblem ID (not slug)

Query Parameters ​

ParameterTypeDefaultDescription
limitinteger20Number of submissions per page
offsetinteger0Number to skip

Response ​

json
{
  "items": [
    {
      "id": "uuid",
      "user_id": "admin",
      "problem_id": "uuid",
      "code": "class Solution:\n    def twoSum(self, nums, target):\n        ...",
      "language": "python",
      "passed": true,
      "runtime_ms": 45,
      "submitted_at": "2024-01-08T10:30:00Z"
    },
    {
      "id": "uuid",
      "user_id": "admin",
      "problem_id": "uuid",
      "code": "class Solution:\n    def twoSum(self, nums, target):\n        ...",
      "language": "python",
      "passed": false,
      "runtime_ms": 12,
      "submitted_at": "2024-01-08T10:15:00Z"
    }
  ],
  "total": 5,
  "limit": 20,
  "offset": 0,
  "has_more": false
}

Example ​

bash
# Get submission history
curl -H "Authorization: Bearer <token>" \
  http://localhost:8000/api/submissions/abc123-uuid

# Pagination
curl -H "Authorization: Bearer <token>" \
  "http://localhost:8000/api/submissions/abc123-uuid?limit=10&offset=0"

Data Models ​

Submit Request ​

typescript
{
  problem_slug: string;
  language: string;
  code: string;
}

Submit Response ​

typescript
{
  success: boolean;
  results: Array<{
    test_number: number;
    passed: boolean;
    input: any[];
    output: any;
    expected: any;
    stdout: string | null;
    error: string | null;
  }>;
  summary: {
    total: number;        // Tests executed
    passed: number;
    failed: number;
  };
  total_test_cases: number;  // Total tests for problem
  submission_id: string;
  runtime_ms: number;
  memory_kb: number;
  compile_error: string | null;
  runtime_error: string | null;
  times_solved: number | null;
  is_mastered: boolean | null;
  next_review_date: string | null;
}

Submission ​

typescript
{
  id: string;
  user_id: string;
  problem_id: string;
  code: string;
  language: string;
  passed: boolean;
  runtime_ms: number;
  submitted_at: string;  // ISO 8601
}

Frontend Integration ​

Submit Solution ​

typescript
import { supabase } from './lib/supabase';

async function submitSolution(
  problemSlug: string,
  code: string,
  language = 'python'
) {
  const { data: { session } } = await supabase.auth.getSession();

  const response = await fetch('/api/submit', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${session?.access_token}`
    },
    body: JSON.stringify({ problem_slug: problemSlug, language, code })
  });

  if (response.status === 401) {
    throw new Error('Authentication required');
  }

  return response.json();
}

// Usage
const result = await submitSolution('two-sum', userCode);

if (result.success) {
  console.log('Accepted! Now rate the difficulty...');
  // Show rating modal, then call rateSubmission()
} else {
  const failedTest = result.results.find(t => !t.passed);
  console.log('Wrong answer on test', failedTest.test_number);
  console.log('Input:', failedTest.input);
  console.log('Expected:', failedTest.expected);
  console.log('Got:', failedTest.output);
}

Rate Submission (Anki SM-2) ​

typescript
async function rateSubmission(submissionId: string, rating: 'again' | 'hard' | 'good' | 'mastered') {
  const { data: { session } } = await supabase.auth.getSession();

  const response = await fetch('/api/rate-submission', {
    method: 'POST',
    headers: {
      'Content-Type': 'application/json',
      'Authorization': `Bearer ${session?.access_token}`
    },
    body: JSON.stringify({ submission_id: submissionId, rating })
  });

  return response.json();
}

// Usage after successful submission
const rateResult = await rateSubmission(result.submission_id, 'good');
console.log(`Next review: ${rateResult.progress.next_review_date}`);
console.log(`Ease factor: ${rateResult.progress.ease_factor}`);

Fetch Submission History ​

typescript
async function getSubmissionHistory(problemId: string) {
  const { data: { session } } = await supabase.auth.getSession();

  const response = await fetch(`/api/submissions/${problemId}`, {
    headers: {
      'Authorization': `Bearer ${session?.access_token}`
    }
  });

  const data = await response.json();
  return data.items;
}

// Usage
const submissions = await getSubmissionHistory(problem.id);
const lastSubmission = submissions[0]; // Most recent first

Display Submission Results ​

typescript
function displayResults(result) {
  if (result.compile_error) {
    return {
      status: 'Compile Error',
      message: result.compile_error
    };
  }

  if (result.runtime_error) {
    return {
      status: 'Runtime Error',
      message: result.runtime_error
    };
  }

  if (result.success) {
    return {
      status: 'Accepted',
      message: `All ${result.total_test_cases} test cases passed`,
      runtime: `${result.runtime_ms} ms`,
      memory: `${(result.memory_kb / 1024).toFixed(2)} MB`
    };
  }

  const failedTest = result.results.find(t => !t.passed);
  return {
    status: 'Wrong Answer',
    message: `Failed on test case ${failedTest.test_number}`,
    testsPassed: `${result.summary.passed} / ${result.total_test_cases}`,
    failedInput: failedTest.input,
    expected: failedTest.expected,
    output: failedTest.output
  };
}

Error Responses ​

401 Unauthorized ​

json
{
  "success": false,
  "error": {
    "code": "AUTHENTICATION_REQUIRED",
    "message": "Authentication required"
  }
}

404 Problem Not Found ​

json
{
  "success": false,
  "error": {
    "code": "PROBLEM_NOT_FOUND",
    "message": "Problem with slug 'invalid' not found"
  }
}

422 Validation Error ​

json
{
  "success": false,
  "error": {
    "code": "VALIDATION_ERROR",
    "message": "Code is required"
  }
}

500 Judge0 Error ​

json
{
  "success": false,
  "error": {
    "code": "JUDGE0_ERROR",
    "message": "Code execution failed",
    "details": "Connection to Judge0 failed"
  }
}

Best Practices ​

Client-Side ​

  1. Show loading state during submission:

    typescript
    const [submitting, setSubmitting] = useState(false);
    
    async function handleSubmit() {
      setSubmitting(true);
      try {
        const result = await submitSolution(slug, code);
        // Handle result
      } finally {
        setSubmitting(false);
      }
    }
  2. Display test count correctly:

    typescript
    const testsPassed = `${result.summary.passed} / ${result.total_test_cases}`;
  3. Cache submissions locally:

    typescript
    localStorage.setItem(`last-code-${problemSlug}`, code);

Server-Side ​

  1. Validate input thoroughly (already implemented)
  2. Rate limit submissions to prevent abuse
  3. Log all submissions for analytics
  4. Clean up old submissions periodically

Next Steps ​