Charity API Lookup
https://cdn.tailwindcss.com
body {
font-family: ‘Inter’, sans-serif;
}
Charity Organization Lookup
Enter EIN (Employer Identification Number):
Enter API Key (Optional, if required by Charity API):
Lookup Organization
document.addEventListener(‘DOMContentLoaded’, () => {
const einInput = document.getElementById(‘einInput’);
const apiKeyInput = document.getElementById(‘apiKeyInput’);
const lookupButton = document.getElementById(‘lookupButton’);
const apiResponsePre = document.getElementById(‘apiResponse’);
const messageBox = document.getElementById(‘messageBox’);
const messageText = document.getElementById(‘messageText’);
// Function to display messages in the message box
function showMessage(message, type = ‘info’) {
messageText.textContent = message;
messageBox.classList.remove(‘hidden’, ‘bg-red-100’, ‘text-red-800’, ‘bg-green-100’, ‘text-green-800’, ‘bg-blue-100’, ‘text-blue-800’);
if (type === ‘error’) {
messageBox.classList.add(‘bg-red-100’, ‘text-red-800’);
} else if (type === ‘success’) {
messageBox.classList.add(‘bg-green-100’, ‘text-green-800’);
} else {
messageBox.classList.add(‘bg-blue-100’, ‘text-blue-800’);
}
messageBox.classList.remove(‘hidden’);
}
// Function to hide the message box
function hideMessage() {
messageBox.classList.add(‘hidden’);
}
lookupButton.addEventListener(‘click’, async () => {
console.log(‘Lookup button clicked!’); // Confirm button click
const ein = einInput.value.trim();
const apiKey = apiKeyInput.value.trim();
hideMessage();
apiResponsePre.textContent = ‘Loading…’;
if (!ein) {
apiResponsePre.textContent = ”;
showMessage(‘Please enter an EIN.’, ‘error’);
return;
}
const apiUrl = `https://api.charityapi.org/api/organizations/${ein}`;
const requestHeaders = {
‘Content-Type’: ‘application/json’
};
// IMPORTANT: Check Charity API documentation for correct API key header/parameter
// Common formats:
// 1. Authorization: Bearer YOUR_API_KEY (used below)
// 2. X-API-Key: YOUR_API_KEY
// 3. API key as a query parameter:
https://api.charityapi.org/api/organizations/${ein}?apiKey=${apiKey}
if (apiKey) {
requestHeaders[‘Authorization’] = `Bearer ${apiKey}`;
// If Charity API uses a different header, change ‘Authorization’ to that header name.
// For example: requestHeaders[‘X-Api-Key’] = apiKey;
}
const timeout = 10000; // 10 seconds timeout
try {
const controller = new AbortController();
const id = setTimeout(() => controller.abort(), timeout);
const response = await fetch(apiUrl, {
method: ‘GET’,
headers: requestHeaders,
signal: controller.signal // Link the AbortController to the fetch request
});
clearTimeout(id); // Clear the timeout if fetch completes within time
console.log(‘Fetch Response Object:’, response); // Log the full response object
if (!response.ok) {
let errorDetails = ”;
const contentType = response.headers.get(‘content-type’);
// Try to parse error body as JSON first, then as text
if (contentType && contentType.includes(‘application/json’)) {
try {
const errorData = await response.json();
errorDetails = JSON.stringify(errorData, null, 2);
} catch (e) {
console.warn(‘Could not parse error response as JSON, trying as text:’, e);
errorDetails = await response.text();
}
} else {
errorDetails = await response.text();
}
console.error(‘API Error Response (Status Code):’, response.status);
console.error(‘API Error Response (Text/Details):’, errorDetails);
throw new Error(`HTTP Error! Status: ${response.status} (${response.statusText}). Details: ${errorDetails || ‘No additional details.’}`);
}
const data = await response.json();
console.log(‘API Success Response Data:’, data);
apiResponsePre.textContent = JSON.stringify(data, null, 2);
showMessage(‘Organization data fetched successfully!’, ‘success’);
} catch (error) {
apiResponsePre.textContent = ‘Error fetching data.’;
console.error(‘Fetch operation failed:’, error); // Log the entire error object
let displayMessage = `Failed to lookup organization: ${error.message}`;
if (error.name === ‘AbortError’) {
displayMessage = “Request timed out. The API did not respond within 10 seconds.”;
} else if (error instanceof TypeError && error.message === ‘Failed to fetch’) {
displayMessage = “Network error or potential CORS issue. Please check your browser’s console for ‘Cross-Origin’ errors in the Network tab.”;
} else if (error.message.includes(“HTTP Error! Status: 401”)) {
displayMessage = “Authentication failed (401 Unauthorized). Please double-check your API key and its required format.”;
} else if (error.message.includes(“HTTP Error! Status: 403”)) {
displayMessage = “Forbidden (403). Access denied. Verify API key, permissions, or rate limits.”;
} else if (error.message.includes(“HTTP Error! Status: 404”)) {
displayMessage = “Organization not found (404). Please verify the EIN is correct.”;
} else if (error.message.includes(“HTTP Error! Status: 500”)) {
displayMessage = “Server error (500). The Charity API might be experiencing issues.”;
}
showMessage(displayMessage, ‘error’);
}
});
});
Rail Tales: Stories Made on the Red Line – July 14, 2011 – 6:43pm
So I started a blog. One of those brand new things that the kids are doing on their interwebs, right? I’ve got my finger on the pulse of technology, right? Of course I do!
This actually isn’t my first venture in blogging. Back in 2005, I had a LiveJournal. By a show of hands, who here remembers LiveJournal? Anyone?
Well, now I’m trying it again. And as many blogs have their own niche, I’m not alone. I have Rail Tales: Stories Made on the Red Line.
Perhaps some explanation is in order. Let’s go over some ground rules:
• These posts must be made from my commute on Boston’s own reliable Red Line. The T, as it’s known around here. I get on at Downtown Crossing and get off at Alewife. Whatever happens in those 20 minutes ends up here.
• Writing starts when I’m on the platform. Bonus points if the train is late.
• My writing should be fairly organic. I’m not spending all day thinking exactly what I’m going to write. That takes away all of the fun that is the spontaneous nature of the Rail Tale.
• Entries are not edited afterwards for grammar, spelling, other remarkable ideas after the fact, etc. It’s speed writing.
• Nothing’s too taboo…except here. Gotta keep it PG. Well, maybe PG-13. Eh…do they still have an X rating? We’ll try to avoid that one, at least.
• You’ll find the full gamut here! Humor, insight, drama, and none of the above. But don’t think it’s like Seinfeld, the blog about nothing. (Though that idea actually caught fire…I’ll rethink that one)
And so why am I doing this? I’m a writer by day, and my goal is that it will keep me sharp. And I really want this to be coherent – not just a complete ramble about nothing (OK – Seinfeld clone blog is officially off the table). Doing a well-written blog in 20 minutes should be a nice challenge, so that’s why I do it. Plus, with all that goes on, from around the world to around the block, I want my voice heard in some way. And blogs are a democratizing tool to do this.
So, in a nutshell, Rail Tales are going to keep me sharp.
Have fun reading and let me know how I’m doing!
Epilogue:
At some point, you may get a super-long blog – the train is terribly delayed, stuck, flying off the rails, on fire (it happens), etc. The Rail Tale can end up being the next great American novel if we’re so lucky. Hooray!
Finally, do you know the theory that, if you give a room full of monkeys a bunch of typewriters and plenty of time, they’ll reproduce Shakespeare? I truly believe that. If, this past Tuesday, you gave a Red Line train full of monkeys a bunch of typewriters, and got it stuck in between Porter and Harvard, that’s plenty of time to reproduce Hamlet. Or at least Macbeth.
And if I was 10 minutes early to Alewife that morning, I would have been on that train with them. I could have charmed them enough to act it out, too, I’m sure. That would have been an entertaining Rail Tale.
Rail Tales: Stories Made on the Red Line – June 23, 2011 – 5:43pm
The MBTA police is out in full force today. Can’t seem to find a reason why.
Now it’s not just the cops standing outside the station, searching your bags for explosives and other devices that go kaboom. They’re IN the station this time – right here in Downtown Crossing. Maybe they came down for a pretzel (though, like Menino until a few weeks ago, I never really fully trust vendor food – on the street, underground, or anywhere else).
One of the cops looks eerily like Ivan Drago, who – as we all know – was the main antagonist in Rocky IV. I hear the entire franchise was good – well, maybe Rocky V was a stretch. But whenever I come across a Rocky movie on TV, it’s almost always Rocky IV. “I must crush you.” Beautiful.
If I was 20 years older and lived through the Cold War as an adult, I always thought that communism’s downfall would be sparked by a bad Philadelphia accent.
Speaking of downfalls, maybe the day-glo yellow vests worn by MBTA’s finest will be the downfall of terrorism. It is a pretty blinding color. And if the terrorists are fashionistas, too, then forget about it. But I would wager that maybe we’re getting it pretty light here in the US of A. I saw an old college friend’s Facebook post the other day, and she said the transit police in Paris were armed to the nines. Not just a pistol on the hip, but machine guns across the chest. Real stuff over there. And given the attack a few years back, you might be able to see why.
It’s tough to police, I’ll admit. When our city moves hundreds of thousands of people each day, that’s a lot to secure. The numbers of shoes you would need to check? Double!
But I would never pass through one of those security checkpoints. They have those dogs out there. And if Homeland Security knows my biggest weakness, it’s a dog in uniform.
So I’ll just have to settle with “See something, say something.”
Which reminds me – I should say something about that 8-foot-tall backpack at Harvard. Looks suspicious to me.