Install any skill in seconds. Free to start, no credit card required.
Get Started Free →IPEDS — primary federal postsecondary data (~6,500 institutions, 1980-present): enrollment, completions, graduation rates, finance, aid, admissions, HR. For college/university analysis. Grad rates = first-time full-time; finance needs GASB/FASB care.
SkillScore82/100
Test results 95 · Live runs 69
3/3 safety checks passed
50%95%of test cases passed +45 pts
Passed 21 of 22 test cases with the skill, and 11 of 22 without it.
10 fixed11 passed either way1 still failing0 broke
One test taskI am reading NCES IPEDS documentation listing raw variable names like INSTNM, CONTROL, ICLEVEL, and EFTOTLT. What are the corresponding column names when accessing these fields in Education Data Portal mirror parquet files?
Adds every file the skill has, pinned so you can update it. No account needed.
pip install "decimalai>=0.15"
decimalai skills add brycewang-stanford-education-data-source-ipeds
claude --plugin-dir "$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.agents"The same install from Python, for a notebook or a setup script.
pip install "decimalai>=0.15"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("brycewang-stanford-education-data-source-ipeds")
if result.exit_code:
raise SystemExit(result.problems)
print("Added. Start Claude Code with the command below.")claude --plugin-dir "$(git rev-parse --show-toplevel 2>/dev/null || pwd)/.agents"Type these in a Claude Code session. No account or API key. It writes the skill and any files it bundles, and asks first when its safety rating isn't Passed.
/plugin marketplace add decimal-labs/decimalai-claude-plugins
/plugin install decimalai@decimalai/decimalai:install brycewang-stanford-education-data-source-ipedsThe plugin needs python3 and curl on your machine.
No Python or account. Claude Code picks it up in its next session.
curl -fsSL --create-dirs -o .claude/skills/brycewang-stanford-education-data-source-ipeds/SKILL.md https://app.decimal.ai/s/brycewang-stanford-education-data-source-ipeds/SKILL.md.claude/skills/brycewang-stanford-education-data-source-ipeds/SKILL.mdThis gets SKILL.md only. The skill's 8 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[claude-agent-sdk]"
decimalai skills add brycewang-stanford-education-data-source-ipedsimport asyncio
from claude_agent_sdk import ClaudeAgentOptions
from decimalai.claude_agent_sdk import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
async def main():
question = "Hello!"
async with support.run(question) as run: # one user message, one run
async for message in run.query(options=ClaudeAgentOptions(max_turns=6)):
print(message)
# In a notebook or async code: await main()
asyncio.run(main())Prefer its own loader? ClaudeAgentOptions(plugins=[skills_plugin()], skills="all") reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[claude-agent-sdk]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("brycewang-stanford-education-data-source-ipeds")
if result.exit_code:
raise SystemExit(result.problems)
import asyncio
from claude_agent_sdk import ClaudeAgentOptions
from decimalai.claude_agent_sdk import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
async def main():
question = "Hello!"
async with support.run(question) as run: # one user message, one run
async for message in run.query(options=ClaudeAgentOptions(max_turns=6)):
print(message)
# In a notebook or async code: await main()
asyncio.run(main())Prefer its own loader? ClaudeAgentOptions(plugins=[skills_plugin()], skills="all") reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.mdimport asyncio
from pathlib import Path
from claude_agent_sdk import ClaudeAgentOptions, query
skill = Path(".agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.md").read_text()
options = ClaudeAgentOptions(system_prompt=f"You are a helpful agent.\n\n{skill}", max_turns=6)
async def main():
async for message in query(prompt="Hello!", options=options):
print(message)
# In a notebook or async code: await main()
asyncio.run(main())This gets SKILL.md only. The skill's 8 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai-agents]"
decimalai skills add brycewang-stanford-education-data-source-ipedsfrom agents import Agent, Runner
from decimalai.openai_agents import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
name=support.name,
instructions=support.instructions, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# In a notebook or async code: result = await Runner.run(agent, "Hello!")
result = Runner.run_sync(agent, "Hello!")Using SandboxAgent? capabilities=[Shell(), decimalai.openai_agents.sandbox_skills()] points its own loader at the same folder.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai-agents]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("brycewang-stanford-education-data-source-ipeds")
if result.exit_code:
raise SystemExit(result.problems)
from agents import Agent, Runner
from decimalai.openai_agents import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
name=support.name,
instructions=support.instructions, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# In a notebook or async code: result = await Runner.run(agent, "Hello!")
result = Runner.run_sync(agent, "Hello!")Using SandboxAgent? capabilities=[Shell(), decimalai.openai_agents.sandbox_skills()] points its own loader at the same folder.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.mdfrom pathlib import Path
from agents import Agent, Runner
skill = Path(".agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.md").read_text()
agent = Agent(name="support", instructions=f"You are a helpful agent.\n\n{skill}")
# In a notebook or async code: result = await Runner.run(agent, "Hello!")
result = Runner.run_sync(agent, "Hello!")This gets SKILL.md only. The skill's 8 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai]"
decimalai skills add brycewang-stanford-education-data-source-ipedsimport json
from openai import OpenAI
from decimalai.openai import agent_config
client = OpenAI()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your tools, as Chat Completions takes them, and what answers each.
TOOLS = [{"type": "function", "function": {
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
# run.system is your prompt, then the skill list.
messages = [{"role": "system", "content": run.system}, run.message]
while True:
reply = client.chat.completions.create(
model="gpt-5.4-mini",
messages=messages,
tools=[*TOOLS, *run.tools], # your tools, then load_skill
).choices[0].message
messages.append(reply)
if not reply.tool_calls:
break
for call in reply.tool_calls:
# load_skill is answered by the run; your own tools by FUNCTIONS.
function = FUNCTIONS.get(call.function.name)
messages.append(run.tool_result(call) or {
"role": "tool", "tool_call_id": call.id,
"content": function(**json.loads(call.function.arguments)) if function
else f"There is no tool named {call.function.name!r}.",
})
print(reply.content)
# The Responses API takes run.response_tools instead of run.tools.The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[openai]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("brycewang-stanford-education-data-source-ipeds")
if result.exit_code:
raise SystemExit(result.problems)
import json
from openai import OpenAI
from decimalai.openai import agent_config
client = OpenAI()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your tools, as Chat Completions takes them, and what answers each.
TOOLS = [{"type": "function", "function": {
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
# run.system is your prompt, then the skill list.
messages = [{"role": "system", "content": run.system}, run.message]
while True:
reply = client.chat.completions.create(
model="gpt-5.4-mini",
messages=messages,
tools=[*TOOLS, *run.tools], # your tools, then load_skill
).choices[0].message
messages.append(reply)
if not reply.tool_calls:
break
for call in reply.tool_calls:
# load_skill is answered by the run; your own tools by FUNCTIONS.
function = FUNCTIONS.get(call.function.name)
messages.append(run.tool_result(call) or {
"role": "tool", "tool_call_id": call.id,
"content": function(**json.loads(call.function.arguments)) if function
else f"There is no tool named {call.function.name!r}.",
})
print(reply.content)
# The Responses API takes run.response_tools instead of run.tools.No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.mdfrom pathlib import Path
from openai import OpenAI
client = OpenAI()
skill = Path(".agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.md").read_text()
reply = client.chat.completions.create(
model="gpt-5.4-mini",
messages=[
{"role": "system", "content": f"You are a helpful agent.\n\n{skill}"},
{"role": "user", "content": "Hello!"},
],
).choices[0].messageThis gets SKILL.md only. The skill's 8 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[anthropic]"
decimalai skills add brycewang-stanford-education-data-source-ipedsimport anthropic
from decimalai.anthropic import agent_config
client = anthropic.Anthropic()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
question = "Hello!"
with support.run(question) as run: # one user message, one run
answer = client.beta.messages.tool_runner(
model="claude-haiku-5-5",
max_tokens=4096,
system=run.system, # your prompt, then the skill list
tools=[*run.tools], # your tools, then load_skill
messages=[{"role": "user", "content": question}],
).until_done()The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[anthropic]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("brycewang-stanford-education-data-source-ipeds")
if result.exit_code:
raise SystemExit(result.problems)
import anthropic
from decimalai.anthropic import agent_config
client = anthropic.Anthropic()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
question = "Hello!"
with support.run(question) as run: # one user message, one run
answer = client.beta.messages.tool_runner(
model="claude-haiku-5-5",
max_tokens=4096,
system=run.system, # your prompt, then the skill list
tools=[*run.tools], # your tools, then load_skill
messages=[{"role": "user", "content": question}],
).until_done()No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.mdfrom pathlib import Path
import anthropic
client = anthropic.Anthropic()
skill = Path(".agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.md").read_text()
reply = client.messages.create(
model="claude-haiku-5-5",
max_tokens=4096,
system=f"You are a helpful agent.\n\n{skill}",
messages=[{"role": "user", "content": "Hello!"}],
)This gets SKILL.md only. The skill's 8 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[google-genai]"
decimalai skills add brycewang-stanford-education-data-source-ipedsfrom google import genai
from google.genai import types
from decimalai.google_genai import agent_config
client = genai.Client()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your functions, as declarations for the model, and what answers each.
TOOLS = [{"function_declarations": [{
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters_json_schema": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}]}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
config = types.GenerateContentConfig(
system_instruction=run.system, # your prompt, then the skill list
tools=[*TOOLS, *run.tool_params], # your functions, then load_skill
)
contents = [{"role": "user", "parts": [{"text": run.message}]}]
while True:
response = client.models.generate_content(
model="gemini-3.5-flash", contents=contents, config=config,
)
if not response.function_calls:
break
contents.append(response.candidates[0].content)
parts = []
for call in response.function_calls:
# load_skill is answered by the run; your own functions by FUNCTIONS.
# A loop of your own rather than automatic function calling, which
# raises KeyError when the model asks for a function you did not pass.
function = FUNCTIONS.get(call.name)
parts.append(run.tool_result(call) or {"function_response": {
"name": call.name, "id": call.id,
"response": {"result": function(**(call.args or {})) if function
else f"There is no function named {call.name!r}."},
}})
contents.append({"role": "user", "parts": parts})
print(response.text)The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[google-genai]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("brycewang-stanford-education-data-source-ipeds")
if result.exit_code:
raise SystemExit(result.problems)
from google import genai
from google.genai import types
from decimalai.google_genai import agent_config
client = genai.Client()
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
def lookup_order(order_id: str) -> str:
return f"Order {order_id} shipped yesterday."
# Your functions, as declarations for the model, and what answers each.
TOOLS = [{"function_declarations": [{
"name": "lookup_order",
"description": "Look up the status of an order.",
"parameters_json_schema": {"type": "object", "properties": {"order_id": {"type": "string"}},
"required": ["order_id"]},
}]}]
FUNCTIONS = {"lookup_order": lookup_order}
question = "Where is order A1001?"
with support.run(question) as run: # one user message, one run
config = types.GenerateContentConfig(
system_instruction=run.system, # your prompt, then the skill list
tools=[*TOOLS, *run.tool_params], # your functions, then load_skill
)
contents = [{"role": "user", "parts": [{"text": run.message}]}]
while True:
response = client.models.generate_content(
model="gemini-3.5-flash", contents=contents, config=config,
)
if not response.function_calls:
break
contents.append(response.candidates[0].content)
parts = []
for call in response.function_calls:
# load_skill is answered by the run; your own functions by FUNCTIONS.
# A loop of your own rather than automatic function calling, which
# raises KeyError when the model asks for a function you did not pass.
function = FUNCTIONS.get(call.name)
parts.append(run.tool_result(call) or {"function_response": {
"name": call.name, "id": call.id,
"response": {"result": function(**(call.args or {})) if function
else f"There is no function named {call.name!r}."},
}})
contents.append({"role": "user", "parts": parts})
print(response.text)No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.mdfrom pathlib import Path
from google import genai
from google.genai import types
client = genai.Client()
skill = Path(".agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.md").read_text()
response = client.models.generate_content(
model="gemini-3.5-flash",
contents="Hello!",
config=types.GenerateContentConfig(system_instruction=f"You are a helpful agent.\n\n{skill}"),
)This gets SKILL.md only. The skill's 8 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[langchain]" langchain-openai
decimalai skills add brycewang-stanford-education-data-source-ipedsfrom langchain.agents import create_agent
from decimalai.langchain import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = create_agent(
"openai:gpt-5.4-mini",
tools=[], # your tools; the middleware adds load_skill
middleware=[support.middleware], # your prompt, the skill list and load_skill
)
result = agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[langchain]" langchain-openaifrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("brycewang-stanford-education-data-source-ipeds")
if result.exit_code:
raise SystemExit(result.problems)
from langchain.agents import create_agent
from decimalai.langchain import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = create_agent(
"openai:gpt-5.4-mini",
tools=[], # your tools; the middleware adds load_skill
middleware=[support.middleware], # your prompt, the skill list and load_skill
)
result = agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.mdfrom pathlib import Path
from langchain.agents import create_agent
skill = Path(".agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.md").read_text()
agent = create_agent("openai:gpt-5.4-mini", tools=[], system_prompt=f"You are a helpful agent.\n\n{skill}")
result = agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})This gets SKILL.md only. The skill's 8 other files are not included.
CrewAI's own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" "crewai>=1.15.27"
decimalai skills add brycewang-stanford-education-data-source-ipedsimport decimalai.crewai
from crewai import Agent, Crew, Task
agent = Agent(
role="Support", goal="Help the customer", backstory="A support agent.",
llm=llm, # your own CrewAI LLM
skills=decimalai.crewai.skills(), # the installed folder
tools=decimalai.crewai.skill_tools(), # reads a skill's files, runs its scripts
)
task = Task(description="Hello!", expected_output="an answer", agent=agent)
Crew(agents=[agent], tasks=[task]).kickoff()The same install from Python, for a notebook or a setup script. CrewAI's own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" "crewai>=1.15.27"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("brycewang-stanford-education-data-source-ipeds")
if result.exit_code:
raise SystemExit(result.problems)
import decimalai.crewai
from crewai import Agent, Crew, Task
agent = Agent(
role="Support", goal="Help the customer", backstory="A support agent.",
llm=llm, # your own CrewAI LLM
skills=decimalai.crewai.skills(), # the installed folder
tools=decimalai.crewai.skill_tools(), # reads a skill's files, runs its scripts
)
task = Task(description="Hello!", expected_output="an answer", agent=agent)
Crew(agents=[agent], tasks=[task]).kickoff()No DecimalAI package. CrewAI's own loader reads the folder, and nothing pins or updates it.
.agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.mdfrom pathlib import Path
from crewai import Agent, Crew, Task
agent = Agent(
role="Support", goal="Help the customer", backstory="A support agent.",
llm=llm, # your own CrewAI LLM
skills=[Path(".agents/skills")],
)
task = Task(description="Hello!", expected_output="an answer", agent=agent)
Crew(agents=[agent], tasks=[task]).kickoff()This gets SKILL.md only. The skill's 8 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[adk]"
decimalai skills add brycewang-stanford-education-data-source-ipedsfrom decimalai.adk import DecimalAIPlugin, agent_config
from google.adk.agents import LlmAgent
from google.adk.apps import App
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = LlmAgent(
name="support", # ADK needs a Python identifier
model="gemini-3.5-flash",
instruction=support.instruction, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# DecimalAIPlugin makes each invocation of runners built from this App one trace.
app = App(name="support", root_agent=agent, plugins=[DecimalAIPlugin()])Prefer ADK's own loader? SkillToolset(skills=decimalai.adk.skills(), ...) reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[adk]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("brycewang-stanford-education-data-source-ipeds")
if result.exit_code:
raise SystemExit(result.problems)
from decimalai.adk import DecimalAIPlugin, agent_config
from google.adk.agents import LlmAgent
from google.adk.apps import App
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = LlmAgent(
name="support", # ADK needs a Python identifier
model="gemini-3.5-flash",
instruction=support.instruction, # your prompt, then the skill list
tools=[*support.tools], # your tools, then load_skill
)
# DecimalAIPlugin makes each invocation of runners built from this App one trace.
app = App(name="support", root_agent=agent, plugins=[DecimalAIPlugin()])Prefer ADK's own loader? SkillToolset(skills=decimalai.adk.skills(), ...) reads the same folder. Use one or the other: a skill delivered both ways reaches the prompt twice.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.mdfrom pathlib import Path
from google.adk.agents import LlmAgent
skill = Path(".agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.md").read_text()
agent = LlmAgent(
name="support",
model="gemini-3.5-flash",
instruction=lambda context: f"You are a helpful agent.\n\n{skill}",
)This gets SKILL.md only. The skill's 8 other files are not included.
agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[pydantic-ai]"
decimalai skills add brycewang-stanford-education-data-source-ipedsfrom pydantic_ai import Agent
from decimalai.pydantic_ai import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
"openai:gpt-4o-mini",
name=support.name,
tools=[], # your tools; the capability adds load_skill
capabilities=[support], # your prompt, the skill list and load_skill
)
# In a notebook or async code: result = await agent.run("Hello!")
result = agent.run_sync("Hello!")On the Pydantic AI harness, decimalai.pydantic_ai_harness.skills(project) uses its own loader instead.
The same install from Python, for a notebook or a setup script. agent_config() hands your agent the skill. With no DecimalAI key it reads your project's files and sends nothing. Needs SDK 0.27 or newer.
pip install "decimalai[pydantic-ai]"from decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("brycewang-stanford-education-data-source-ipeds")
if result.exit_code:
raise SystemExit(result.problems)
from pydantic_ai import Agent
from decimalai.pydantic_ai import agent_config
# With no DECIMAL_API_KEY this reads the skills your project lists, from disk.
support = agent_config("support", fallback="You are a helpful agent.")
agent = Agent(
"openai:gpt-4o-mini",
name=support.name,
tools=[], # your tools; the capability adds load_skill
capabilities=[support], # your prompt, the skill list and load_skill
)
# In a notebook or async code: result = await agent.run("Hello!")
result = agent.run_sync("Hello!")On the Pydantic AI harness, decimalai.pydantic_ai_harness.skills(project) uses its own loader instead.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.mdfrom pathlib import Path
from pydantic_ai import Agent
skill = Path(".agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.md").read_text()
agent = Agent("openai:gpt-4o-mini", instructions=f"You are a helpful agent.\n\n{skill}")
# In a notebook or async code: result = await agent.run("Hello!")
result = agent.run_sync("Hello!")This gets SKILL.md only. The skill's 8 other files are not included.
Deep Agents' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" deepagents
decimalai skills add brycewang-stanford-education-data-source-ipedsimport decimalai.deepagents
from deepagents import create_deep_agent
agent = create_deep_agent(model=model, **decimalai.deepagents.skills())
agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})The default backend reads files but has no shell. To run a skill's scripts, pass skills(backend=LocalShellBackend(...)).
The same install from Python, for a notebook or a setup script. Deep Agents' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" deepagentsfrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("brycewang-stanford-education-data-source-ipeds")
if result.exit_code:
raise SystemExit(result.problems)
import decimalai.deepagents
from deepagents import create_deep_agent
agent = create_deep_agent(model=model, **decimalai.deepagents.skills())
agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})The default backend reads files but has no shell. To run a skill's scripts, pass skills(backend=LocalShellBackend(...)).
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.mdfrom pathlib import Path
from deepagents import create_deep_agent
skill = Path(".agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.md").read_text()
agent = create_deep_agent(model=model, system_prompt=f"You are a helpful agent.\n\n{skill}")
agent.invoke({"messages": [{"role": "user", "content": "Hello!"}]})This gets SKILL.md only. The skill's 8 other files are not included.
Its own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" agent-framework
decimalai skills add brycewang-stanford-education-data-source-ipedsimport asyncio
from agent_framework import Agent
from decimalai.agent_framework import skills
agent = Agent(client=client, instructions="You are a helpful agent.", context_providers=[skills()])
asyncio.run(agent.run("Hello!"))It caches skills by default, so restart the process after decimalai skills update.
The same install from Python, for a notebook or a setup script. Its own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" agent-frameworkfrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("brycewang-stanford-education-data-source-ipeds")
if result.exit_code:
raise SystemExit(result.problems)
import asyncio
from agent_framework import Agent
from decimalai.agent_framework import skills
agent = Agent(client=client, instructions="You are a helpful agent.", context_providers=[skills()])
asyncio.run(agent.run("Hello!"))It caches skills by default, so restart the process after decimalai skills update.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.mdimport asyncio
from pathlib import Path
from agent_framework import Agent
skill = Path(".agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.md").read_text()
agent = Agent(client=client, instructions=f"You are a helpful agent.\n\n{skill}")
asyncio.run(agent.run("Hello!"))This gets SKILL.md only. The skill's 8 other files are not included.
Strands' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" strands-agents
decimalai skills add brycewang-stanford-education-data-source-ipedsfrom decimalai.strands import skills
from strands import Agent
from strands.vended_tools import file_editor, shell
agent = Agent(
model=model, # always pass one: the default is Amazon Bedrock
system_prompt="You are a helpful agent.",
tools=[file_editor, shell],
plugins=[skills()],
)
agent("Hello!")Strands loads an agent's skills once, so build the agent again after decimalai skills update.
The same install from Python, for a notebook or a setup script. Strands' own skill loader reads the installed folder. No DecimalAI account needed.
pip install "decimalai>=0.16" strands-agentsfrom decimalai import skillpkg
# Writes .agents/skills and .decimal/skills.toml. Safe to run twice.
result = skillpkg.add("brycewang-stanford-education-data-source-ipeds")
if result.exit_code:
raise SystemExit(result.problems)
from decimalai.strands import skills
from strands import Agent
from strands.vended_tools import file_editor, shell
agent = Agent(
model=model, # always pass one: the default is Amazon Bedrock
system_prompt="You are a helpful agent.",
tools=[file_editor, shell],
plugins=[skills()],
)
agent("Hello!")Strands loads an agent's skills once, so build the agent again after decimalai skills update.
No DecimalAI package. The whole skill goes in every prompt, and nothing pins or updates it.
.agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.mdfrom pathlib import Path
from strands import Agent
skill = Path(".agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.md").read_text()
agent = Agent(model=model, system_prompt=f"You are a helpful agent.\n\n{skill}")
agent("Hello!")This gets SKILL.md only. The skill's 8 other files are not included.
Adds every file the skill has, pinned so you can update it. Cursor, Copilot, Cline and Warp read this folder by themselves.
pip install "decimalai>=0.15"
decimalai skills add brycewang-stanford-education-data-source-ipedsNo Python or account. Cursor, Copilot, Cline and Warp read this folder by themselves.
curl -fsSL --create-dirs -o .agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.md https://app.decimal.ai/s/brycewang-stanford-education-data-source-ipeds/SKILL.md.agents/skills/brycewang-stanford-education-data-source-ipeds/SKILL.mdThis gets SKILL.md only. The skill's 8 other files are not included.
Give its text to your model as instructions. No account or package needed.
This gets SKILL.md only. The skill's 8 other files are not included.
Raw files: /s/brycewang-stanford-education-data-source-ipeds/SKILL.md · /s/brycewang-stanford-education-data-source-ipeds.json
Fetch the file and give its text to your model as instructions. No account or package needed.
curl -fsSL https://app.decimal.ai/s/brycewang-stanford-education-data-source-ipeds/SKILL.mdcurl -fsSL https://app.decimal.ai/s/brycewang-stanford-education-data-source-ipeds.jsonThis gets SKILL.md only. The skill's 8 other files are not included.
You don't need a DecimalAI account.
Saved prompts and skills, pinned versions, and traces of this skill in your runs. DecimalAI does not run your agent.
Claude Code reads files from your project, so DecimalAI config does not apply there.
On Claude Agent SDK: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On OpenAI Agents: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On OpenAI SDK: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Anthropic: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Gemini API: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On LangChain: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On CrewAI: set DECIMAL_API_KEY and add decimalai.init(crewai=True) to record runs and skill use. The skill version stays in your repo.
On Google ADK: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Pydantic AI: save the skill to an agent here, set DECIMAL_API_KEY, and the same code uses it with no command to run. Changes you save reach the next run with no redeploy.
On Deep Agents: set DECIMAL_API_KEY and add decimalai.init(langchain=True) to record runs and skill use. The skill version stays in your repo.
On Microsoft Agent Framework: set DECIMAL_API_KEY and add decimalai.init(otel=True, agent_name="support") to record runs and skill use. The skill version stays in your repo.
On Strands: set DECIMAL_API_KEY and add decimalai.init(otel=True, agent_name="support") to record runs and skill use. The skill version stays in your repo.
Editors read files from your project, so DecimalAI config does not apply there.
Anywhere else: record a use with decimalai.log_skill_activation() inside a trace.
IPEDS (Integrated Postsecondary Education Data System) — the primary federal data system for ~6,500 U.S. postsecondary institutions, comprising 12+ annual survey components: enrollment, completions, graduation rates, finance, financial aid, admissions, human resources, and institutional characteristics (1980-present, varies by component). Use when analyzing postsecondary enrollment, degree completions by CIP code, institutional finances, or admissions data. Graduation rates track first-time full-time students only (150% cohort). Cross-sector finance comparisons require care due to GASB vs. FASB accounting.
Comprehensive guide to understanding and using IPEDS data correctly. IPEDS is the most widely used source for postsecondary education data but has significant complexities — including sector-specific accounting standards, cohort-limited graduation rates, and integer-encoded categorical variables — that users must understand.
CRITICAL: Value Encoding
This document describes Education Data Portal integer encodings, which differ from NCES raw file string codes. The Portal converts categorical variables to integers for consistency across sources.
Context Race White Race Black Sex Male Sector Public 4-yr Portal (integers) 1211NCES raw files EFFY_WHITEEFFY_BKAAMvaries Always verify codes against Portal codebooks (available alongside each dataset in the Portal mirrors).
IPEDS (Integrated Postsecondary Education Data System) is a system of 12+ interrelated survey components:
| File | Purpose | When to Read |
|---|---|---|
survey-components.md | All 12+ IPEDS surveys with collection periods | Understanding data structure |
graduation-rates.md | CRITICAL GRS limitations and who is tracked | Any graduation rate analysis |
enrollment-data.md | Fall vs 12-month, FTE calculations | Enrollment comparisons |
finance-data.md | GASB vs FASB accounting standards | Cross-sector finance analysis |
financial-aid.md | Net price, aid types, populations | Aid and cost analysis |
institution-identifiers.md | UNITID, OPEID, mergers, closures | Data linking and longitudinal work |
completions-data.md | Degrees awarded, CIP codes | Completions and outcomes |
data-quality.md | Known issues, sector comparisons | Quality assurance |
Working with IPEDS data?
├─ Graduation rates → ./references/graduation-rates.md (READ FIRST!)
├─ Enrollment counts → ./references/enrollment-data.md
├─ Finance/revenue/expenses → ./references/finance-data.md
├─ Financial aid/net price → ./references/financial-aid.md
├─ Degrees/completions → ./references/completions-data.md
├─ Institutional info → ./references/survey-components.md (IC section)
├─ Human resources/salaries → ./references/survey-components.md (HR section)
└─ Linking to other data → ./references/institution-identifiers.md
Cross-sector comparison?
├─ Comparing grad rates across sectors
│ └─ CAUTION: Different populations → ./references/graduation-rates.md
├─ Comparing finances across sectors
│ └─ CAUTION: GASB vs FASB → ./references/finance-data.md
├─ Comparing net price across sectors
│ └─ CAUTION: Aid populations differ → ./references/financial-aid.md
└─ Time series analysis
└─ Check for institutional changes → ./references/institution-identifiers.md
Need variable definitions?
├─ Survey component overview → ./references/survey-components.md
├─ Graduation cohort definitions → ./references/graduation-rates.md
├─ Enrollment level/status → ./references/enrollment-data.md
├─ Revenue/expense categories → ./references/finance-data.md
├─ Aid types and populations → ./references/financial-aid.md
└─ CIP codes for programs → ./references/completions-data.md
| Component | Abbrev | Collection | Key Content |
|---|---|---|---|
| Institutional Characteristics | IC | Fall | Directory, tuition, mission |
| 12-Month Enrollment | E12 | Fall | Unduplicated headcount, FTE |
| Completions | C | Fall | Degrees by CIP, demographics |
| Cost | CST | Fall/Winter | Cost of attendance, net price |
| Admissions | ADM | Winter | Applications, admits, enrollees |
| Student Financial Aid | SFA | Winter | Aid counts and amounts |
| Graduation Rates | GR | Winter | 150% completion rates |
| Graduation Rates 200% | GR200 | Winter | 200% completion rates |
| Outcome Measures | OM | Winter | Part-time and transfer outcomes |
| Fall Enrollment | EF | Spring | Point-in-time enrollment |
| Finance | F | Spring | Revenue, expenses, assets |
| Human Resources | HR | Spring | Employees, salaries |
| Academic Libraries | AL | Spring | Library resources (biennial) |
| ID | Format | Level | Example | Notes |
|---|---|---|---|---|
unitid | 6-digit integer | Institution | 100654 | Unique, persistent across years; changes on merger |
opeid | 8-digit string | Institution (Title IV) | 00100200 | Links to FSA/NSLDS; shared across branches |
| Variable | Values | Meaning |
|---|---|---|
inst_control | 1 | Public |
| 2 | Private nonprofit | |
| 3 | Private for-profit | |
| -1 | Missing/not reported | |
institution_level | 1 | Less than 2-year |
| 2 | 2-year (at least 2 but less than 4) | |
| 4 | 4-year or above | |
| -1 | Missing/not reported | |
sector | 0 | Administrative unit |
| 1 | Public, 4-year or above | |
| 2 | Private not-for-profit, 4-year or above | |
| 3 | Private for-profit, 4-year or above | |
| 4 | Public, 2-year | |
| 5 | Private not-for-profit, 2-year | |
| 6 | Private for-profit, 2-year | |
| 7 | Public, less-than 2-year | |
| 8 | Private not-for-profit, less-than 2-year | |
| 9 | Private for-profit, less-than 2-year | |
| -1 | Sector unknown (not active) | |
hbcu | 1 | Historically Black College/University |
| 0 | Not HBCU | |
| -1 | Missing/not reported | |
tribal_college | 1 | Tribal College |
| 0 | Not Tribal College | |
| -1 | Missing/not reported | |
degree_granting | 1 | Degree-granting |
| 0 | Non-degree-granting |
Note: There is no code 3 for institution_level. The Portal uses codes 1, 2, 4 (not 1, 2, 3).
inst_size Categories| Code | Meaning |
|---|---|
| 1 | Under 1,000 |
| 2 | 1,000 - 4,999 |
| 3 | 5,000 - 9,999 |
| 4 | 10,000 - 19,999 |
| 5 | 20,000 and above |
Note: inst_size is a category code (1-5), not an actual enrollment count.
| Code | Category | Notes |
|---|---|---|
1 | White | Single race, non-Hispanic |
2 | Black | Single race, non-Hispanic |
3 | Hispanic | Any race |
4 | Asian | Single race, non-Hispanic |
5 | American Indian/Alaska Native | Single race, non-Hispanic |
6 | Native Hawaiian/Pacific Islander | Single race, non-Hispanic |
7 | Two or more races | Multiple races selected, non-Hispanic |
8 | Nonresident alien | International students |
9 | Unknown | Race/ethnicity unknown |
20 | Other | Other race/ethnicity |
99 | Total | All races combined |
-1 | Missing/not reported | |
-2 | Not applicable | |
-3 | Suppressed | Privacy protection |
Historical note: Prior to 2010, Asian included Pacific Islanders (code 6 did not exist), and "Two or more races" (code 7) was not collected.
| Code | Category |
|---|---|
1 | Male |
2 | Female |
3 | Nonbinary/Another gender |
4 | Unknown/Prefer not to say |
9 | Unknown |
99 | Total |
-1 | Missing/not reported |
-2 | Not applicable |
-3 | Suppressed |
Note: Codes 3 and 4 are recent additions for non-binary gender reporting. Historical data may only have codes 1, 2, and 99. The exact meaning of codes 3 vs 4 may vary by endpoint — check the specific codebook.
| Code | Meaning | When Used |
|---|---|---|
-1 | Missing/not reported | Data not submitted by institution |
-2 | Not applicable | Item doesn't apply to this institution type |
-3 | Suppressed | Data suppressed for privacy |
null | Not available | Field not collected for this survey year |
| Data Type | Year Field Meaning |
|---|---|
| Institutional characteristics | As of fall of indicated year |
| Fall enrollment | As of fall census date |
| 12-month enrollment | July 1 to June 30 academic year |
| Completions | Awarded during academic year |
| Graduation rates | Cohort entered in indicated year |
| Finance | Fiscal year ending in indicated year |
| Student financial aid | For indicated academic year |
Datasets for IPEDS are available via the mirror system. See datasets-reference.md for canonical paths, mirrors.yaml for mirror configuration, and fetch-patterns.md for fetch code patterns.
Key datasets:
| Dataset | Type | Path | Codebook |
|---|---|---|---|
| Directory | Single | ipeds/colleges_ipeds_directory | ipeds/codebook_colleges_ipeds_directory |
| Admissions | Single | ipeds/colleges_ipeds_admissions-enrollment | ipeds/codebook_colleges_ipeds_admissions-enrollment |
| Enrollment FTE | Single | ipeds/colleges_ipeds_enrollment-fte | ipeds/codebook_colleges_ipeds_enrollment-fte |
| Graduation Rates | Single | ipeds/colleges_ipeds_grad-rates | ipeds/codebook_colleges_ipeds_grad-rates |
| Finance | Single | ipeds/colleges_ipeds_finance | ipeds/codebook_colleges_ipeds_finance |
32 IPEDS datasets exist in the mirror (5 shown above). See datasets-reference.md for the complete list with all paths and codebook paths.
Known Portal gaps:
- Distance education enrollment variables (
efdeexc,efdesom,efdenom) are not in Portal mirror datasets. Use the NCES IPEDS Data Center for these.- Open-admissions policy variable (
OPENADMP) is not in Portal mirror datasets. Note:open_publicis NOT the same thing — see Common Pitfalls below.- Finance data may have a year lag relative to NCES releases (last verified through 2017 in some datasets).
For data not available through Portal mirrors, access NCES directly at https://nces.ed.gov/ipeds/.
Codebooks are .xls files co-located with data in all mirrors. Use get_codebook_url() from fetch-patterns.md to construct download URLs:
url = get_codebook_url("ipeds/codebook_colleges_ipeds_directory")
Truth Hierarchy: When interpreting variable values, apply this priority:
- Actual data file (what you observe in the parquet/CSV) — this IS the truth
- Live codebook (.xls in mirror) — authoritative documentation, may lag
- This skill documentation — convenient summary, may drift from codebook
If this documentation contradicts the codebook, trust the codebook. If the codebook contradicts observed data, trust the data and investigate.
import polars as pl
# Admissions totals: filter to sex=99 for institution-level totals
# WRONG - includes duplicates (~26K rows with multiple sex values per institution)
df = pl.read_parquet("data/raw/admissions.parquet")
# CORRECT - one row per institution-year (~8K rows)
df_totals = df.filter(pl.col("sex") == 99)
# Calculate admission rate (not provided directly)
df = df.with_columns(
(pl.col("number_admitted") / pl.col("number_applied") * 100).alias("admit_rate")
)
# Filter to active, degree-granting, 4-year public institutions
df = df.filter(
(pl.col("sector") == 1) &
(pl.col("degree_granting") == 1)
)
IPEDS data becomes available with significant lag. Always verify year availability before committing to a year range.
| Survey Component | Typical Lag | Latest Available (as of Jan 2026) |
|---|---|---|
| Directory | ~1 year | 2023 |
| Admissions-Enrollment | ~2 years | 2022 |
| Fall Enrollment | ~2-3 years | 2022 |
| Completions | ~2 years | 2022 |
| Finance | ~4+ years | 2017 (see warning below) |
| Graduation Rates | ~2-3 years | 2022 |
CRITICAL: IPEDS Finance Data Cutoff. As of January 2026, IPEDS Finance data is only available through 2017 in the Portal mirrors. This affects endowment values (
endowment_end), revenue/expense data, and any financial ratios. Options: (1) limit analysis to available years, (2) use NCCS 990 data for private institutions as an alternative, or (3) forward-fill with a documented caveat and indicator column.
The Portal uses different names than NCES raw file documentation. The table below lists commonly confused mappings:
| NCES Raw File Name | Actual Portal Name | Notes |
|---|---|---|
INSTNM | inst_name | Institution name |
STABBR | state_abbr | State abbreviation |
CONTROL | inst_control | Institutional control |
ICLEVEL | institution_level | Level of institution |
DEGGRANT | degree_granting | Degree-granting status |
CYACTIVE | currently_active_ipeds | Currently active flag |
DEATHYR | year_deleted | Year institution closed |
APPLCN | number_applied | Total applicants |
ADMSSN | number_admitted | Total admitted |
EFTOTLT | enrollment_fall | Fall enrollment (in fall-enrollment-race dataset) |
various GR* | completion_rate_150pct, completers_150pct, etc. | Grad rate variables |
Note: Portal variable names are always lowercase with underscores. NCES documentation often uses UPPERCASE or CamelCase. When in doubt, fetch a sample of the actual data and inspect its column names.
IPEDS has multiple enrollment-related datasets in the Portal:
| Dataset | Key Columns | Best For |
|---|---|---|
fall-enrollment-race (yearly) | enrollment_fall, race, sex, level_of_study, ftpt, class_level, degree_seeking | Detailed demographic breakdowns |
fall-enrollment-age (yearly) | Enrollment by age group | Age distribution analysis |
enrollment-fte (single) | est_fte, rep_fte | FTE-based comparisons |
enrollment-headcount (single) | Headcount data | Headcount-based analysis |
fall-retention (single) | Retention rates | Retention analysis |
Note: The
fall-enrollment-raceyearly dataset provides the most granular enrollment data, disaggregated by multiple dimensions. For institution-level totals, filter torace == 99,sex == 99,ftpt == 99,level_of_study == 99.
| Pitfall | Issue | Solution |
|---|---|---|
| Using string codes | Portal uses integer encodings, not NCES string codes | Always verify against Portal codebooks; see encoding table above |
| Grad rates as sole quality metric | IPEDS tracks only first-time, full-time, fall-entering students; excludes ~40% transfers, ~40% part-time | Use Outcome Measures (OM) for part-time/transfer data; note limitations |
| Cross-sector finance comparison | Public (GASB) and private (FASB) use different accounting standards | Compare within sector only; see ./references/finance-data.md for crosswalk |
| Net price for all students | Net price covers only first-time, full-time students who received Title IV aid | Document population limitation; excludes full-pay students |
| Admissions without sex filter | Admissions data disaggregated by sex — unfiltered data has duplicates | Filter to sex == 99 for institution totals |
No institution_level 3 | Codes are 1, 2, 4 — not sequential 1, 2, 3 | Use exact codes: 1=less-than-2yr, 2=2yr, 4=4yr+ |
| Ignoring mergers/closures | Institutions merge, close, or change sector over time | Check currently_active_ipeds and year_deleted; track UNITID changes; see ./references/institution-identifiers.md |
inst_size as enrollment | inst_size is a 1-5 category code, not an enrollment count | Use enrollment endpoints for actual counts |
| Distance education variables missing | efdeexc, efdesom, efdenom are not in Portal mirror datasets | Use the NCES IPEDS Data Center directly for distance education enrollment |
| GRS duplicate rows per institution | Graduation rates data has multiple rows per unitid within the same subcohort/year, differing in cohort_rev and count columns | Filter to target subcohort first (e.g., subcohort == 2 for bachelor's-seeking at 4-yr), then deduplicate: sort by completion_rate_150pct descending (nulls last), then unique(subset=["unitid"], keep="first") |
open_public is not open admissions | open_public (from openpubl) means "open to the general public" (i.e., a currently operating institution) — Harvard has open_public=1. The actual open-admissions policy variable (OPENADMP) is not available in the Portal mirror | Do not use open_public to identify open-admissions institutions. Use admissions data (admit rate near 100%) as a proxy, or access OPENADMP via the IPEDS API directly |
SFA type_of_aid=9 is all grants, not Pell | type_of_aid=9 in sfa_grants_and_net_price captures ALL grant/scholarship recipients (Pell + institutional + state/local). The median ratio to total students is ~0.98 — nearly universal. This dramatically overestimates "Pell share" if used as a Pell proxy | For Pell-specific data, use FSA (pre-2020) or College Scorecard bulk download. SFA type_of_aid=9 is appropriate for total grant aid analysis but not for Pell isolation |
CRITICAL: IPEDS graduation rates track ONLY first-time, full-time, fall-entering students.
| Excluded Population | Approximate % of Undergrads |
|---|---|
| Transfer students | ~40% |
| Part-time students | ~40% |
| Spring/summer starts | Varies |
| Students who transfer OUT | Counted as non-completers |
At community colleges, IPEDS grad rates may represent <25% of students.
See ./references/graduation-rates.md for complete details.
CRITICAL: Public and private institutions use different accounting standards.
| Standard | Institution Type | Comparison |
|---|---|---|
| GASB | Public | Compare within sector only |
| FASB | Private nonprofit | Different from GASB |
| FASB | Private for-profit | Different revenue treatment |
See ./references/finance-data.md for crosswalk guidance.
Net price is calculated ONLY for:
See ./references/financial-aid.md for details.
import polars as pl
def ipeds_quality_check(df):
"""Basic IPEDS data quality checks using Portal variable names."""
issues = []
# Check graduation rates — Portal stores as 0-1 proportions (not 0-100)
# See education-data-context skill > Rate and Proportion Normalization
if "completion_rate_150pct" in df.columns:
bad = df.filter(
(pl.col("completion_rate_150pct") > 1.0) |
(pl.col("completion_rate_150pct") < 0)
)
if bad.height > 0:
issues.append(f"Invalid grad rates: {bad.height} rows")
# Check for non-active institutions (directory dataset)
if "currently_active_ipeds" in df.columns:
inactive = df.filter(pl.col("currently_active_ipeds") != 1)
if inactive.height > 0:
issues.append(f"Non-active institutions: {inactive.height}")
# Check sector consistency
if "inst_control" in df.columns:
invalid = df.filter(
~pl.col("inst_control").is_in([1, 2, 3, -1])
)
if invalid.height > 0:
issues.append(f"Invalid control codes: {invalid.height}")
return issues
| Source | Relationship | When to Use |
|---|---|---|
education-data-source-scorecard | Non-traditional student outcomes | Post-college earnings, broader student population |
education-data-source-fsa | Detailed loan/grant data | Federal student aid analysis (link on OPEID) |
education-data-source-nccs | Private institution 990 data | Financial data beyond IPEDS cutoff year |
education-data-source-pseo | Post-college employment | State-level employment outcomes |
education-data-source-eada | College athletics | Athletics equity and finance |
education-data-source-nacubo | Endowment data | Endowment analysis beyond IPEDS |
education-data-source-campus-safety | Campus crime statistics | Safety and compliance |
education-data-explorer | Parent discovery skill | Finding available endpoints |
education-data-query | Data fetching | Downloading parquet/CSV files |
| Topic | Reference File |
|---|---|
| Survey components overview | ./references/survey-components.md |
| Graduation rate cohort definition | ./references/graduation-rates.md |
| First-time full-time limitation | ./references/graduation-rates.md |
| Transfer-out rates | ./references/graduation-rates.md |
| Outcome Measures survey | ./references/graduation-rates.md |
| 150% vs 200% time | ./references/graduation-rates.md |
| Fall enrollment | ./references/enrollment-data.md |
| 12-month enrollment | ./references/enrollment-data.md |
| FTE calculations | ./references/enrollment-data.md |
| Enrollment by level | ./references/enrollment-data.md |
| GASB accounting | ./references/finance-data.md |
| FASB accounting | ./references/finance-data.md |
| Revenue categories | ./references/finance-data.md |
| Expense categories | ./references/finance-data.md |
| Net price definition | ./references/financial-aid.md |
| Pell grant data | ./references/financial-aid.md |
| Aid by income level | ./references/financial-aid.md |
| UNITID | ./references/institution-identifiers.md |
| OPEID | ./references/institution-identifiers.md |
| Institutional mergers | ./references/institution-identifiers.md |
| Sector changes | ./references/institution-identifiers.md |
| CIP codes | ./references/completions-data.md |
| Award levels | ./references/completions-data.md |
| Completers vs completions | ./references/completions-data.md |
| Data quality issues | ./references/data-quality.md |
| Missing data codes | ./references/data-quality.md |
| Sector comparisons | ./references/data-quality.md |
| Subcohort codes (GRS) | ./references/graduation-rates.md |
| GRS deduplication | ./references/graduation-rates.md |
open_public vs open admissions | Common Pitfalls (this file) |
SFA type_of_aid codes | ./references/financial-aid.md |
Skills that do similar work, with their headline benchmark lift.
From the same source and nearby in the catalogue.