ECB Data API SDMX: series key, dimension và kiểm đơn vị
ECB Data API SDMX là cách lấy chuỗi số của Ngân hàng Trung ương châu Âu kèm cấu trúc mô tả từng mã. Bài này viết cho người cần series đúng và biết rõ đơn vị, không chỉ tải một bảng số. ProxyVN đã gọi API thật ngày 05/11/2026, thử key sai thứ tự, ngày nghỉ, và đọc được chỗ một chuỗi lạm phát đã ngừng mà vẫn trả số.
ECB Data API SDMX: mục tiêu và phạm vi không lặp
Phần này chốt bài toán, đầu ra và chỗ khác với các bài dữ liệu kinh tế còn lại trong cụm.
Bài toán cần giải
Một nhóm phân tích cần tỷ giá trung bình tháng và một vài chỉ tiêu khu vực đồng euro. Họ tải tệp CSV từ cổng dữ liệu, xóa các cột "thừa" rồi đưa vào báo cáo. Vài tháng sau không ai nói được cột đó là trung bình hay cuối kỳ, đơn vị là gì, và chuỗi còn cập nhật không. Bảng số mất nghĩa khi tách khỏi cấu trúc của nó.
Đầu ra nghiệm thu
Đầu ra là bộ dữ liệu gồm dataflow, series key, từng dimension đã tách, thời gian, giá trị, trạng thái quan sát, đơn vị và phiên bản cấu trúc. Đi kèm là một phép kiểm đơn vị. Người đọc cầm một dòng bất kỳ phải dựng lại được đúng truy vấn đã sinh ra nó.
Ranh giới với Frankfurter và World Bank
Bài Frankfurter trong cụm này tra nhanh tỷ giá theo ngày. Bài này đọc cấu trúc phía sau chuỗi số, nên không chỉ dừng ở một lệnh GET lấy bảng. Bài World Bank API dùng mô hình chỉ tiêu theo quốc gia, còn SDMX dùng khóa nhiều chiều. Chiều nào đứng ở vị trí nào mới là điều bài này muốn làm rõ.
Giới hạn của lab
Lab gọi data-api.ecb.europa.eu khoảng 30 lượt, không khóa, nghỉ ba giây. Lab tập trung vào dataflow EXR và thử vài key của ICP, BSI. Cổng dữ liệu trả 504 vài lần trong ngày lab chạy, kể cả với key đúng, nên một số phép thử được lặp. Bài không phân tích kinh tế, không dự báo, không tư vấn đầu tư.
Ai nên đọc
Bài hợp với người làm phân tích kinh tế, kỹ sư dữ liệu nối nguồn thống kê châu Âu, sinh viên viết đề tài cần trích dẫn số liệu đúng. Ai chỉ cần tỷ giá hôm nay thì bài Frankfurter nhanh hơn. Ai làm việc với nhiều nguồn SDMX khác như Eurostat sẽ dùng lại được phần đọc cấu trúc.
Những gì bài không làm
Bài không giới thiệu toàn bộ chuẩn SDMX. Bài không đi vào mọi dataset của ECB. Bài cũng không giải thích ý nghĩa kinh tế của từng chỉ tiêu ngoài mức cần để đọc đúng cột.
Thuật ngữ dùng trong bài
SDMX là chuẩn trao đổi dữ liệu thống kê. Dataflow là một tập dữ liệu có tên, như EXR. Data structure definition, viết tắt DSD, mô tả các dimension và attribute. Dimension là một chiều của khóa. Attribute là thông tin đi kèm chuỗi hoặc quan sát. Codelist là danh sách mã hợp lệ của một chiều. Series key là các giá trị dimension nối bằng dấu chấm.
Lấy series ECB qua SDMX và đọc dimensions, unit thay vì CSV không schema với ECB Data API SDMX
Phần này đi từ cú pháp URL tới response đầu tiên, rồi cho thấy các cột mà bảng số rút gọn đã bỏ mất.
Nguồn tài liệu đã đọc
Lab đọc trang tổng quan API của ECB Data Portal và trang Data của API. Trang Data mô tả cú pháp gồm entry point, resource, flowRef, key và các tham số startPeriod, endPeriod, updatedAfter, firstNObservations, lastNObservations, detail, includeHistory. Trang tổng quan có thêm mục metadata, schema, content negotiation và status code.
Lời gọi đầu tiên
Entry point là https://data-api.ecb.europa.eu/service/. Lab gọi dataflow EXR, key D.USD.EUR.SP00.A, định dạng CSV. Tham số detail=dataonly bỏ các attribute để nhìn gọn, nhưng lát nữa sẽ thấy vì sao không nên dùng nó cho bảng lưu trữ.
import requests, csv, io
s = requests.Session()
s.headers["User-Agent"] = "ProxyVN-docs-lab/1.0 (+https://proxy.vn/)"
E = "https://data-api.ecb.europa.eu/service"
r = s.get(E + "/data/EXR/D.USD.EUR.SP00.A", timeout=90, params={
"startPeriod": "2026-10-29", "endPeriod": "2026-11-03",
"format": "csvdata", "detail": "dataonly"})
print(r.status_code)
for x in csv.DictReader(io.StringIO(r.text)):
print(x["KEY"], x["TIME_PERIOD"], x["OBS_VALUE"])
Output thật là bốn dòng: 29/10 là 1.113, 30/10 là 1.1172, 02/11 là 1.1216, 03/11 là 1.1179. Ngày 31/10 và 01/11 là cuối tuần nên không có dòng.
Bản đầy đủ có gì
Bỏ detail=dataonly thì CSV có 32 cột. Ngoài các chiều còn có OBS_STATUS, OBS_CONF, OBS_PRE_BREAK, OBS_COM, TIME_FORMAT, COLLECTION, DECIMALS, SOURCE_AGENCY, TITLE, TITLE_COMPL, UNIT, UNIT_MULT và nhiều cột khác. Chuỗi USD có UNIT là USD, UNIT_MULT 0, DECIMALS 4, COLLECTION A. Tên đầy đủ TITLE_COMPL ghi tỷ giá tham chiếu ECB, US dollar/Euro, 2.15 pm C.E.T. Toàn bộ ngữ nghĩa nằm ở những cột người ta hay xóa.
Vì sao CSV rút gọn nguy hiểm
Tệp chỉ gồm ngày và giá trị không cho biết con số là đô la trên một euro hay ngược lại. Nó không nói là giá trị cuối ngày hay trung bình, cũng không nói có phải số ước tính không. Khi ghép nhiều chuỗi vào một bảng, sự khác biệt này biến mất. Lưu cấu trúc cùng dữ liệu mới giữ được ý nghĩa.
JSON có cấu trúc khác
Với header Accept application/vnd.sdmx.data+json, API trả SDMX-JSON. Các giá trị attribute trong đó là chỉ số trỏ vào danh sách ở khối structure, không phải chuỗi. Observation của lab là [1.1267, 0, 0, None, None]: phần tử đầu là giá trị, các phần tử sau là chỉ số của OBS_STATUS, OBS_CONF, OBS_PRE_BREAK, OBS_COM theo đúng thứ tự khai báo. Đọc nhầm thứ tự thì trạng thái sai mà không có lỗi.
r = s.get(E + "/data/EXR/D.USD.EUR.SP00.A", timeout=90,
params={"lastNObservations": 1},
headers={"Accept": "application/vnd.sdmx.data+json;version=1.0.0-wd"})
j = r.json()
oa = j["structure"]["attributes"]["observation"]
ser = next(iter(j["dataSets"][0]["series"].values()))
t, ob = next(iter(ser["observations"].items()))
for i, a in enumerate(oa):
k = ob[i + 1]
print(a["id"], None if k is None else a["values"][k])
Đoạn này giải mã chỉ số thành giá trị thật. Lab khuyên dùng CSV đầy đủ cho phân tích thường ngày, vì đọc dễ hơn nhiều, và chỉ dùng SDMX-JSON khi công cụ của anh em cần cấu trúc này.
Trang lỗi không đồng nhất
Trong ngày lab chạy, API trả nhiều kiểu lỗi. Có lúc là JSON application/problem+json, có lúc là trang HTML của ECB với mã 400 hoặc 504. Script không được giả định thân lỗi là JSON. Kiểm content-type trước, rồi ghi 300 ký tự đầu vào log.
504 là chuyện cần tính tới
Trong lần chạy của lab, một số request trả 504 rồi lần sau lại 200, kể cả cùng một key. Yêu cầu dataflow kèm references=all và detail=full trả 504. Yêu cầu datastructure với phiên bản latest trả 504, còn phiên bản 1.0 trả 200. Vì vậy cần thử lại có giãn cách, và nên xin cấu trúc nhỏ thay vì gom tất cả vào một lời gọi.
import time
def get_retry(url, tries=4, **k):
for i in range(tries):
r = s.get(url, timeout=90, **k)
if r.status_code not in (502, 503, 504):
return r
time.sleep(5 * (i + 1))
r.raise_for_status()
Hàm chỉ thử lại lỗi phía máy chủ, không thử lại 400 hay 404. Lỗi khách hàng gửi lại bao nhiêu lần cũng vẫn sai.
Khám phá dataflow và thứ tự dimensions SDMX trong ECB Data API SDMX
Muốn dựng key đúng, anh em phải đọc cấu trúc trước. Đoán từ một URL mẫu sẽ sai ngay khi đổi dataset.
Đọc dataflow

Thứ tự dimension
Lab gọi /dataflow/ECB/EXR/latest và nhận XML nhỏ 990 byte, cho biết Dataflow ECB:EXR phiên bản 1.0. Tệp này chỉ khai báo tên. Muốn biết các chiều, anh em đọc data structure của nó, là ECB_EXR1.
Đọc data structure
Lab gọi /datastructure/ECB/ECB_EXR1/1.0 và nhận XML 24416 byte. Bên trong khai báo năm dimension theo vị trí: FREQ 1, CURRENCY 2, CURRENCY_DENOM 3, EXR_TYPE 4, EXR_SUFFIX 5. TIME_PERIOD là chiều thời gian ở vị trí 6, không nằm trong key. Thứ tự position này quyết định thứ tự trong key.
import re
r = get_retry(E + "/datastructure/ECB/ECB_EXR1/1.0",
headers={"Accept": "application/vnd.sdmx.structure+xml;version=2.1"})
print(re.findall(r'Dimension[^>]*?id="(\w+)"[^>]*?position="(\d+)"', r.text))
Output thật là FREQ 1, CURRENCY 2, CURRENCY_DENOM 3, EXR_TYPE 4, EXR_SUFFIX 5, TIME_PERIOD 6. Lab dùng regex để minh họa nhanh. Trong dự án thật, anh em đọc bằng trình phân tích XML hoặc thư viện như sdmx1.
Các attribute được khai báo
Cùng tệp đó liệt kê TIME_FORMAT, OBS_STATUS, OBS_CONF, OBS_PRE_BREAK, OBS_COM, BREAKS, COLLECTION, COMPILING_ORG, DISS_ORG, DOM_SER_IDS, PUBL_ECB, PUBL_MU, PUBL_PUBLIC, UNIT_INDEX_BASE, COMPILATION, COVERAGE, DECIMALS, NAT_TITLE, SOURCE_AGENCY, SOURCE_PUB, TITLE, TITLE_COMPL, UNIT, UNIT_MULT. Đó là lý do CSV đầy đủ có nhiều cột trống: attribute được khai báo cho cả dataflow, chuỗi nào có thì điền.
Đọc codelist
Lab gọi /codelist/ECB/CL_EXR_SUFFIX/latest và thấy các mã A là Average, E là End-of-period, P là Growth rate to previous period, R là Annual rate of change, S và T là các tỷ lệ thay đổi khác. Codelist CL_EXR_TYPE có 36 mã, trong đó SP00 là Spot, EN00 là tỷ giá hiệu dụng danh nghĩa, ERC0 là tỷ giá hiệu dụng thực giảm phát theo CPI.
Đọc key thành câu
Key D.USD.EUR.SP00.A đọc thành: tần suất ngày, đồng USD, tính theo EUR, loại Spot, biến thể A. Tên dễ gây hiểu nhầm ở đây là A, nghĩa Average. Với chuỗi ngày, trung bình của một ngày chính là con số tham chiếu của ngày đó. Với chuỗi tháng, A thật sự là trung bình các ngày.
Bảng năm chiều của key mẫu
Vị trí |
Dimension |
Giá trị |
Nghĩa |
Codelist |
|---|---|---|---|---|
1 |
FREQ |
D |
Ngày |
Tần suất |
2 |
CURRENCY |
USD |
Đô la Mỹ |
Tiền tệ |
3 |
CURRENCY_DENOM |
EUR |
Tính theo euro |
Tiền tệ |
4 |
EXR_TYPE |
SP00 |
Spot |
CL_EXR_TYPE |
5 |
EXR_SUFFIX |
A |
Average |
CL_EXR_SUFFIX |
Tại sao cấu trúc có phiên bản
DSD có số phiên bản, ở đây là 1.0. Khi nguồn đổi cấu trúc, số phiên bản giúp biết bảng cũ được tạo theo bản nào. Trong yêu cầu thật, gọi latest tiện nhưng khó tái lập, và trong lab nó còn trả 504. Bảng lưu trữ nên ghi đúng số phiên bản đang dùng.
Mỗi dataflow một cấu trúc
Key EXR không áp được cho ICP hay BSI. ICP có các chiều FREQ, REF_AREA, ADJUSTMENT, ICP_ITEM, STS_INSTITUTION, ICP_SUFFIX. BSI có 11 chiều, gồm BS_REP_SECTOR, BS_ITEM, MATURITY_ORIG và các chiều khác. Lab thấy thứ tự này ngay trên dòng tiêu đề CSV, đây là cách kiểm nhanh nhất khi chưa đọc DSD.
Tạo series key đúng dataset đang chọn với ECB Data API SDMX
Key là một chuỗi ngắn nhưng rất dễ sai. Mục này dùng các lỗi thật để lập danh sách cần kiểm.
Key sai thứ tự

Key sai không báo rõ
Lab đảo hai chiều đầu thành USD.D.EUR.SP00.A. Lần đầu nhận 504, lần sau nhận 404 với thông báo No Series was returned. API không nói rằng thứ tự sai, chỉ nói không có chuỗi. Người mới thường nghĩ dữ liệu không tồn tại.
Key thiếu chiều
Lab gọi D.USD.EUR.SP00 với bốn phần thay vì năm và nhận HTTP 400 dưới dạng trang HTML của ECB. Key phải đủ số chiều, kể cả khi anh em muốn để trống một chiều. Với chiều không chọn, key giữ dấu chấm và để trống phần đó.
Đồng tiền không có trong dataset
Lab gọi D.VND.EUR.SP00.A và nhận 404 cùng thông báo. ECB không công bố tỷ giá tham chiếu cho VND trong tập này, phù hợp với kết quả v1 của Frankfurter. Một mã hợp lệ về mặt ISO không có nghĩa là có chuỗi trong dataset.
Kết hợp hợp lệ khác kết hợp có dữ liệu
Lab gọi D.USD.EUR.EN00.A và nhận 404. Mã EN00 có trong codelist, nhưng không có chuỗi USD theo EUR với tần suất ngày cho loại này. Codelist nói mã nào hợp lệ, không nói tổ hợp nào tồn tại. Phải dùng truy vấn thăm dò để biết.
Dùng ô trống để thăm dò
Lab gọi D..EUR.SP00.A với detail=serieskeysonly và nhận 44 chuỗi, tức mỗi đồng tiền một khóa. Cách này cho biết nhanh danh sách đồng tiền có tỷ giá ngày, mà không tải giá trị. Đây là công cụ khám phá chính khi làm với dataset lạ.
r = get_retry(E + "/data/EXR/D..EUR.SP00.A",
params={"lastNObservations": 1, "format": "csvdata",
"detail": "serieskeysonly"})
rows = list(csv.DictReader(io.StringIO(r.text)))
print(len(rows), [x["CURRENCY"] for x in rows[:6]])
Output thật là 44 chuỗi, mở đầu bằng ARS, AUD, BGN, BRL. Danh sách này nên lưu cùng ngày kiểm, vì ECB có thể ngừng hoặc thêm đồng.
Nhiều giá trị trong một chiều
Dấu cộng cho phép xin nhiều giá trị. Lab gọi D.USD+JPY.EUR.SP00.A với lastNObservations=1 và nhận hai dòng: JPY 178.3 và USD 1.1267, cùng ngày 04/11. Một request lấy nhiều chuỗi giúp giảm tải. Khi đó phải đọc cột KEY để tách từng chuỗi, không dựa vào thứ tự.
Ngày không có quan sát
Lab gọi khoảng 31/10 tới 01/11 và nhận HTTP 200 với thân rỗng, 0 byte. Khoảng ngày trong tương lai cũng vậy. Đọc thân rỗng bằng csv.DictReader không lỗi, chỉ ra danh sách rỗng, nên script phải phân biệt "không có quan sát" với "tải lỗi". Ở đây mã 200 và độ dài 0 là một trạng thái hợp lệ.
Ngày lễ
Khoảng 24 tới 29/12/2025 chỉ có hai dòng: 24/12 là 1.1787 và 29/12 là 1.1766. Các ngày 25 và 26/12 là ngày nghỉ, 27 và 28 là cuối tuần. API không điền số, khác với cách Frankfurter trả ngày trước khi hỏi đơn lẻ. Khoảng trống là dữ liệu đúng, không phải lỗi tải.
Hàm dựng key theo cấu trúc
Thay vì ghép chuỗi tay, anh em truyền dict theo tên chiều, hàm tự sắp theo vị trí và báo lỗi nếu thừa thiếu. Sai thứ tự không còn xảy ra được.
DIMS = {"EXR": ["FREQ", "CURRENCY", "CURRENCY_DENOM", "EXR_TYPE", "EXR_SUFFIX"]}
def make_key(flow, **kw):
d = DIMS[flow]
extra = set(kw) - set(d)
if extra:
raise ValueError(f"chieu la: {extra}")
return ".".join("+".join(kw[x]) if isinstance(kw.get(x), list) else kw.get(x, "") for x in d)
print(make_key("EXR", FREQ="D", CURRENCY=["USD", "JPY"], CURRENCY_DENOM="EUR",
EXR_TYPE="SP00", EXR_SUFFIX="A"))
print(make_key("EXR", FREQ="D", CURRENCY_DENOM="EUR", EXR_TYPE="SP00", EXR_SUFFIX="A"))
Hai dòng in ra là D.USD+JPY.EUR.SP00.A và D..EUR.SP00.A, đúng với hai request đã chạy ở trên. Bảng DIMS nên sinh tự động từ DSD trong dự án lớn.
Đọc observation value, unit và status metadata trong ECB Data API SDMX
Giá trị chỉ có nghĩa khi đi cùng các attribute. Phần này giải thích những cột phải giữ.
Trung bình và cuối kỳ
Đây là ví dụ rõ nhất cho việc một chữ cuối key đổi con số. Với tần suất tháng, lab gọi hai chuỗi và thấy cùng một tháng có hai giá trị khác nhau.
Tháng |
M.USD.EUR.SP00.A, Average |
M.USD.EUR.SP00.E, End-of-period |
|---|---|---|
09/2026 |
1.1513 |
1.1355 |
10/2026 |
1.1202 |
1.1172 |
Đọc khác biệt
Tháng 9 chênh gần 0.016. Giá trị E của tháng 10 là 1.1172, đúng bằng tỷ giá ngày 30/10, ngày làm việc cuối tháng. Giá trị A của tháng 10 có đuôi dài 1.1201590909090908 vì là trung bình các ngày. Cột COLLECTION cũng đổi theo, A và E. Nếu xóa cột này, không ai phân biệt được hai số.
Số lẻ dài và DECIMALS
Chuỗi tháng trả nhiều chữ số dù attribute DECIMALS là 4. DECIMALS là số chữ số nên hiển thị, không phải độ chính xác lưu. Lưu nguyên giá trị, chỉ làm tròn khi trình bày. Lớp hiển thị đọc DECIMALS là đẹp nhất.
OBS_STATUS
Lab thấy OBS_STATUS là A trong mọi dòng EXR đã đọc. Codelist CL_OBS_STATUS có A Normal value, B Time series break, D Definition differs, E Estimated value, F Forecast value, G Experimental value, H Missing value do ngày lễ hoặc cuối tuần và nhiều mã khác. Giá trị E hay F không nên xếp cùng số thực tế mà không có ghi chú.
FLAG = {"E": "uoc_tinh", "F": "du_bao", "B": "dut_chuoi", "H": "thieu"}
def status_note(x):
return FLAG.get(x.get("OBS_STATUS", ""), "")
r = get_retry(E + "/data/EXR/M.USD.EUR.SP00.A",
params={"lastNObservations": 2, "format": "csvdata"})
for x in csv.DictReader(io.StringIO(r.text)):
print(x["TIME_PERIOD"], x["OBS_VALUE"], x["OBS_STATUS"], repr(status_note(x)), x["UNIT"], x["UNIT_MULT"])
Với chuỗi tỷ giá tháng, status_note rỗng vì trạng thái là A. Danh sách FLAG chỉ chọn các mã lab đọc được, anh em mở rộng theo codelist khi dùng dataset khác.
UNIT và UNIT_MULT
UNIT cho biết đơn vị, UNIT_MULT là số mũ của 10. Giá trị 0 nghĩa là không nhân. Với dữ liệu tài chính, UNIT_MULT 6 nghĩa là triệu. Phép kiểm đơn vị của lab là mỗi series phải có đúng một cặp UNIT, UNIT_MULT, và mọi chuỗi trong một cột của bảng phải cùng cặp đó.
def check_units(rows):
seen = {}
for x in rows:
seen.setdefault(x["KEY"], set()).add((x["UNIT"], x["UNIT_MULT"]))
bad = {k: v for k, v in seen.items() if len(v) != 1}
if bad:
raise ValueError(bad)
return {k: next(iter(v)) for k, v in seen.items()}
Hàm này trả cặp đơn vị của từng series. Lab đọc hai chuỗi USD và JPY: UNIT lần lượt là USD và JPY, UNIT_MULT đều 0, DECIMALS là 4 và 2. Hai chuỗi có đơn vị khác nhau nên không được cộng hay vẽ chung trục.
Cột UNIT_INDEX_BASE gây hiểu nhầm
Chuỗi tỷ giá có UNIT_INDEX_BASE ghi 99Q1=100 dù giá trị không phải chỉ số. Lab không tìm thấy giải thích trong phần đã đọc. Một trường metadata không phải lúc nào cũng áp dụng rõ ràng cho từng chuỗi. Khi thấy trường lạ, anh em ghi lại rồi hỏi nguồn, không suy diễn thành đổi giá trị.
Chuỗi đã ngừng vẫn trả số
Đây là phát hiện quan trọng. Lab gọi ICP.M.U2.N.000000.4.ANR với lastNObservations và nhận quan sát cuối là 2025-12, giá trị 1.9, đơn vị PCCH. Dòng 2025-11 có cột OBS_COM ghi rằng từ 04/02/2026 lạm phát HICP đổi phương pháp và ECB ngừng dataset ICP, thay bằng dataset HICP mới. API trả HTTP 200 và số trông bình thường, chỉ có cột chú thích mới nói chuỗi đã dừng. Bản detail=dataonly sẽ xóa đúng cột đó.
Kiểm độ mới của chuỗi
Mỗi lần tải, anh em so ngày quan sát cuối với tần suất của chuỗi. Chuỗi tháng mà cuối cùng là 12/2025 trong khi hôm nay là 11/2026 thì có vấn đề. Phép kiểm chỉ vài dòng nhưng bắt được đúng ca ICP này.
Lưu nhãn mã và phiên bản cấu trúc để query tái lập với ECB Data API SDMX
Phần này giúp một người khác chạy lại đúng truy vấn sau nhiều tháng.
Tệp định nghĩa truy vấn

Lưu để tái lập
Mỗi series trong dự án nên có một dòng cấu hình: dataflow, phiên bản cấu trúc, key, tần suất, định nghĩa ngắn và ngày thêm. Tệp này nằm trong kho mã nguồn. Bảng dữ liệu chỉ cần trỏ về tệp đó. Bất kỳ ai đọc đều biết con số được định nghĩa thế nào.
Lưu nhãn cùng mã
Mã USD dễ hiểu, nhưng SP00 hay N000000 thì không. Lab tải codelist dưới dạng XML và lưu nhãn tiếng Anh theo ngày. Nhãn lưu một lần là đủ cho báo cáo, không cần gọi lại. Nếu nhãn đổi theo phiên bản, tệp lưu sẽ cho thấy thay đổi.
Tải tăng dần bằng updatedAfter
Tham số updatedAfter chỉ trả quan sát thay đổi sau một mốc. Lab gọi với mốc 03/11 và nhận hai dòng 03/11 và 04/11, kèm cột ACTION là Replace. Cột này cho biết dòng đó thay thế dòng cũ theo khóa. Tải tăng dần vì vậy phải dùng upsert, không phải thêm mới.
r = get_retry(E + "/data/EXR/D.USD.EUR.SP00.A", params={
"updatedAfter": "2026-11-03T00:00:00+01:00",
"format": "csvdata", "detail": "dataonly"})
print(r.text)
Requests tự mã hóa dấu cộng thành %2B trong params, nên chạy đúng. Viết tay vào URL thì dấu cộng dễ thành khoảng trắng và bị từ chối, một lỗi rất khó nhìn ra.
Header cache
Response có Cache-Control max-age=30 và Last-Modified là 04/11 lúc 14:57:27 GMT. Lab gửi If-Modified-Since muộn hơn mốc đó mà vẫn nhận 200 với nội dung đầy đủ, không phải 304, ở lần thử đó. Vì vậy updatedAfter là cách tăng dần an toàn hơn so với dựa vào header.
Bản ghi sửa đổi
Tham số includeHistory cho thấy các lần sửa của một quan sát theo tài liệu, lab chưa thử. Số thống kê có thể được chỉnh sau khi công bố, tỷ giá tham chiếu thì hiếm hơn. Cột fetched_at giúp biết bảng được lấy khi nào, mối quan tâm này được bàn sâu hơn trong bài FRED vintage của cùng cụm.
Thư viện hỗ trợ
Gói sdmx1 cho Python hỗ trợ nhiều nguồn SDMX và lab đã cài bản 2.28.0, nhưng chưa dùng cho phần đo trong bài vì lỗi 504 ở các endpoint cấu trúc khiến kết quả khó tái lập. Requests kèm CSV đủ cho bài này. Dự án nhiều dataset nên xem xét dùng thư viện để đọc DSD.
Proxy có cần không
API công khai, không khóa, gọi thẳng là cách đúng. Lỗi 504 của lab nằm ở phía máy chủ, không phải lý do để đổi IP. Thử lại có giãn cách và xin ít dữ liệu hơn mỗi lần mới là cách xử lý. Nhịp gọi xem thêm ở bài rate limit API khi dùng proxy.
Khi mạng doanh nghiệp bắt buộc
Nếu mạng buộc đi qua cổng ra, khai báo ở session, chuỗi theo định dạng proxy. Việc đọc cấu trúc và kiểm đơn vị chạy offline trên tệp đã lưu. Bài Web Scraping proxy là gì phân biệt các vai trò này.
Tác vụ khác cần IP ổn định thì anh em có thể mua proxy giá rẻ tại trang chủ ProxyVN.
So với Frankfurter để kiểm chéo
Lab so năm ngày 29/10 tới 04/11 giữa chuỗi D.USD.EUR.SP00.A và Frankfurter v1, cả năm ngày đều khớp. Phép so này hữu ích khi anh em dùng API gọn cho ứng dụng và cần một nguồn gốc để đối soát. Nếu hai bên lệch, người phải giải thích là bên trung gian. Nguồn gốc luôn là ECB Data Portal.
Viết test cho truy vấn
Mỗi series trong cấu hình nên có một test nhỏ: gọi lastNObservations=1, kiểm có dòng, kiểm UNIT đúng như đã ghi, kiểm ngày đủ mới. Test chạy theo lịch hằng tuần và báo khi một chuỗi ngừng. Với ICP, test này sẽ báo sau vài tháng không có số mới, thay vì để báo cáo dùng số cũ lâu dài.
Trình bày cho người đọc không chuyên
Biểu đồ nên ghi tên chuỗi bằng lời thường, kèm key nhỏ ở chú thích. Ví dụ "USD trên 1 EUR, trung bình tháng, nguồn ECB", dưới cùng là key đầy đủ. Người đọc thường hiểu, người kiểm tra tra lại được. Đây là cách giữ cả hai nhu cầu trên một trang.
Ghi nguồn
Dữ liệu từ ECB Data Portal cần ghi tên nguồn và ngày truy cập theo điều khoản của họ. Với chuỗi đã ngừng, nên ghi cả dataset thay thế. Anh em đọc điều khoản dùng lại dữ liệu trước khi công bố.
Đầu ra nghiệm thu dataflow, series key, dimensions, observation status và kiểm đơn vị của ECB Data API SDMX
Mục này chốt bảng và danh sách kiểm.
Schema của bảng

Nghiệm thu series
Cột |
Ví dụ thật |
Ghi chú |
|---|---|---|
dataflow |
EXR |
Tên tập dữ liệu |
dsd_version |
ECB_EXR1 1.0 |
Tái lập |
key |
D.USD.EUR.SP00.A |
Đủ năm chiều |
time_period |
2026-11-04 |
Theo tần suất |
obs_value |
1.1267 |
Nguyên văn |
obs_status |
A |
Normal value |
unit |
USD |
Từ attribute |
unit_mult |
0 |
Số mũ của 10 |
collection |
A |
Trung bình hay cuối kỳ |
obs_com |
rỗng |
Giữ nguyên |
fetched_at |
2026-11-05 |
Ngày tải |
Danh sách kiểm
- Key: dựng từ DSD, đủ số chiều, đúng vị trí.
- Thăm dò: dùng serieskeysonly để biết tổ hợp nào tồn tại.
- Attribute: không dùng detail=dataonly cho bảng lưu trữ.
- Đơn vị: mỗi series đúng một cặp UNIT và UNIT_MULT.
- Độ mới: ngày quan sát cuối phù hợp tần suất.
- Chú thích: đọc OBS_COM, vì chuỗi ngừng vẫn trả số.
Câu kết luận được phép viết
Từ lab có thể viết rằng thứ tự dimension sai cho 404 không gợi ý nguyên nhân. Ngày không có quan sát trả 200 rỗng. Average và End-of-period khác nhau rõ ràng cho cùng tháng. Chuỗi ICP chỉ báo dừng trong OBS_COM. Không nên viết về chất lượng dịch vụ từ vài lỗi 504 của một ngày.
Những gì lab chưa chạy
Lab chưa thử includeHistory, firstNObservations, chưa dùng sdmx1 để đọc dataset. Lab chưa đọc dataset HICP thay thế, chưa kiểm chính sách dữ liệu đầy đủ. Lab chỉ đọc ba dataset, nên các nhận xét về cột có thể khác ở dataset khác.
Series key cho biết chuỗi nào, attribute cho biết con số nghĩa là gì. Xóa attribute là giữ con số nhưng mất ý nghĩa của nó.
ECB Data API SDMX: câu hỏi thường gặp
Các câu dưới đây gom những điểm hay bị hỏi.
Có cần khóa API không?
Không. Lab gọi mọi endpoint không khóa, chỉ dùng User-Agent nhận diện.
Vì sao key đúng mã vẫn 404?
Có thể sai thứ tự chiều, hoặc tổ hợp đó không có chuỗi. Dùng ô trống và serieskeysonly để thăm dò.
HTTP 200 mà thân rỗng nghĩa là gì?
Khoảng thời gian đó không có quan sát, ví dụ cuối tuần hoặc tương lai.
Có nên dùng detail=dataonly không?
Dùng khi xem nhanh. Bảng lưu trữ cần attribute để giữ đơn vị, trạng thái và chú thích.
Hậu tố A nghĩa là gì?
Average. Với chuỗi tháng là trung bình các ngày, khác E là cuối kỳ.
Sao chuỗi lạm phát dừng ở tháng 12/2025?
Chú thích OBS_COM ghi ECB ngừng dataset ICP từ 04/02/2026 và thay bằng dataset HICP.
Nên chọn CSV hay JSON?
CSV đầy đủ dễ đọc hơn. SDMX-JSON dùng chỉ số và cần giải mã.
Gặp 504 thì làm gì?
Thử lại có giãn cách, xin ít hơn mỗi lần, dùng phiên bản cụ thể cho cấu trúc.
ECB Data API SDMX và những điều cần nhớ
ECB Data API SDMX cho số đáng tin khi anh em dựng key từ cấu trúc, giữ đơn vị, trạng thái, chú thích và phiên bản, thay vì chỉ lưu bảng ngày và giá trị. Proxy.vn - Nhà cung cấp dịch vụ proxy chất lượng hàng đầu Việt Nam khuyên anh em đọc OBS_COM mỗi khi tải chuỗi từ ECB Data API SDMX.