CSV to QTI Converter: Turn Spreadsheet Question Banks into Canvas, Moodle, and Brightspace Quizzes
FO
Formswrite Team
May 19, 2026
CSV to QTI Converter: Spreadsheet Question Banks into LMS-Ready Quiz Packages
If you keep your question bank in a spreadsheet - a CSV pulled from a question-authoring tool, an exam committee's shared Google Sheet, or a textbook publisher's instructor materials - converting it into a quiz inside Canvas, Moodle, Brightspace, or Blackboard is a known pain. The LMS wants a QTI 2.1 .zip, your data is in rows and columns, and the schemas do not line up out of the box.
This guide shows the column conventions Formswrite's CSV-to-QTI converter expects, how to convert a single spreadsheet through the web app, and a small Python recipe for batch conversion through the API.
Why a CSV-to-QTI converter exists
QTI (Question and Test Interoperability) is the open standard every major LMS imports. A QTI package is a
.zip of XML files plus an imsmanifest.xml index. The format is verbose: a single multiple-choice question in QTI 2.1 takes roughly 25 lines of XML. A 200-question bank is 5,000 lines.Writing that by hand is impractical. Most people start from a spreadsheet because spreadsheets are how exam committees actually work. The job of a CSV-to-QTI converter is to translate a flat tabular layout into the QTI tree and bundle it into a valid package the LMS will accept.
CSV column conventions
The Formswrite converter accepts a flexible layout. The minimum viable CSV has four columns:
| Column | Required? | Notes |
|---|---|---|
question | Yes | The question stem. Markdown is allowed. |
type | No | Defaults to multiple_choice. Other values: true_false, short_answer, essay, multiple_response, fill_blank, matching. |
choice_a, choice_b, choice_c, … | Conditional | Required for multiple choice / multiple response. As many as you need. |
correct | Yes | The correct choice letter(s). For multiple response, comma-separate (a,c). |
points | No | Defaults to 1. |
feedback_correct, feedback_incorrect | No | Optional per-question feedback strings. |
image_url | No | A public URL to an image displayed above the question. |
A sample row looks like this:
csvquestion,type,choice_a,choice_b,choice_c,choice_d,correct,points
"What is the capital of France?",multiple_choice,"Berlin","Madrid","Paris","Rome",c,1
"Photosynthesis produces oxygen.",true_false,,,,,true,1
"Name the largest planet in the solar system.",short_answer,,,,,Jupiter,2
The parser handles a few alternate header names automatically (
stem for question, answer for correct, etc.) so a CSV exported from another LMS is usually accepted without renaming.Convert through the web (single CSV)
- Open formswrite.com, sign in.
- Upload the CSV.
- Review the parsed questions. Anything ambiguous is flagged inline.
- Export as QTI 2.1 (.zip).
- Import the zip into your LMS (see per-LMS steps below).
This path is the fastest way to convert a single spreadsheet. For 50+ CSVs, switch to the API.
Convert through the API (batch)
Below is a working Python script that walks a folder of CSV files and converts each to a QTI package. Useful when migrating an entire course catalog.
pythonimport requests
from pathlib import Path
API_TOKEN = "your_formswrite_api_token"
INPUT_DIR = Path("./csv_question_banks")
OUTPUT_DIR = Path("./qti_packages")
OUTPUT_DIR.mkdir(exist_ok=True)
for csv_path in INPUT_DIR.glob("*.csv"):
with csv_path.open("rb") as f:
response = requests.post(
"https://api.formswrite.com/v1/convert",
headers={"Authorization": f"Bearer {API_TOKEN}"},
files={"file": (csv_path.name, f, "text/csv")},
data={"output_format": "qti_2_1"},
timeout=120,
)
response.raise_for_status()
out_path = OUTPUT_DIR / f"{csv_path.stem}.zip"
out_path.write_bytes(response.content)
print(f"converted {csv_path.name} -> {out_path.name}")
For very large workbooks, swap the synchronous call for the async endpoint (
/v1/convert?async=true) and poll the job until ready.Importing the QTI file
Canvas
Settings → Import Course Content → Content Type: QTI .zip file → Upload → Import. Questions land in Quizzes → Question Banks.
Moodle
Course → Question bank → Import → IMS QTI 2.1 format → Upload zip → Import.
Brightspace (D2L)
Course Admin → Import / Export / Copy Components → Import → select QTI zip. Full Brightspace-specific steps and gotchas in the Brightspace quiz import guide.
Blackboard Ultra
Course Content → Tests, Pools, and Surveys → Pools → Import Pool → upload zip.
Excel and Google Sheets
If your question bank lives in Excel (.xlsx) or a Google Sheet, the same converter accepts those formats directly - no need to export to CSV first. The same column conventions apply. For Google Sheets, connect your Google account once and pick the spreadsheet from the picker.
Common CSV pitfalls
- Smart quotes (
"curly quotes") from Word or Google Docs sometimes break naive CSV parsers. Formswrite handles them, but if you are using another converter, save the CSV as UTF-8 plain text first. - Empty trailing rows can confuse some importers. Trim before uploading.
- Commas inside question text must be enclosed in double quotes. Most spreadsheet apps do this automatically on export.
- Different LMSes round point values differently - if you specify
0.5points per question, Canvas keeps the decimal; older Blackboard versions round to integers.
CSV-to-QTI vs. CSV-to-LMS-native
You can sometimes skip QTI entirely. Canvas accepts a Canvas-flavored CSV format directly (one column per question type). Moodle has its own GIFT format. If you are committed to a single LMS forever, the LMS-native format may be shorter to learn.
QTI is the right choice when:
- You support more than one LMS (university with multiple departments, or a publisher distributing to schools).
- You want your question bank portable between LMSes in the future.
- You want to round-trip - pull questions out of one LMS, edit, push to another.
FAQ
Is the CSV-to-QTI converter free?
Single conversions are free. Bulk and API use is paid.
What QTI version does the converter output?
QTI 2.1 by default. QTI 2.2 is supported on request - useful for platforms that specifically require it.
Will it preserve images referenced by URL in my CSV?
Yes - images at the URL in
image_url are downloaded and embedded into the QTI package so they render even if the original URL goes down.Can it handle math / equations in CSV cells?
Yes - LaTeX between
$…$ or \(…\) is converted to MathML so the LMS renders it as a real equation.My CSV uses non-standard column names. Do I have to rename everything?
No - the parser maps common alternates automatically (
prompt, stem, answer_text, is_correct, etc.). Anything it cannot map is flagged in the preview.Related guides
- Don't have a CSV question bank yet? The AI Quiz Generator drafts questions from a PDF, Word doc, or slide deck - and exports straight to QTI without the CSV step.
- Generating from a PDF specifically? The PDF to Quiz Generator is the PDF-focused variant - automatic OCR, equation handling, page-level source traceability.
- Target is Google Forms instead of QTI? PDF to Google Forms is the direct PDF → Google Forms path.
- QTI Converter Online: Free Tool to Convert Any Document to QTI - multi-format conversion overview.
- Canvas Quiz Converter: From Word, PDF, or Text - Canvas-specific workflow.
- Brightspace Quiz Import from Word - D2L-specific workflow.
- QTI Format Explained - what is inside a QTI .zip and which LMSes accept which version.
- Bulk Convert Google Docs to LMS Quizzes - Python script for batch course migrations.
Ready to transform your documents?
Convert Google Docs, PDFs, and spreadsheets into forms with one click.
Start Now →Or go straight to a converter: Google Docs to Forms, PDF to Google Form, Word to Google Form, Google Forms Quiz Generator